xref: /aosp_15_r20/external/grpc-grpc/examples/python/xds/server.py (revision cc02d7e222339f7a4f6ba5f422e6413f4bd931f2)
1# Copyright 2020 The gRPC authors.
2#
3# Licensed under the Apache License, Version 2.0 (the "License");
4# you may not use this file except in compliance with the License.
5# You may obtain a copy of the License at
6#
7#     http://www.apache.org/licenses/LICENSE-2.0
8#
9# Unless required by applicable law or agreed to in writing, software
10# distributed under the License is distributed on an "AS IS" BASIS,
11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12# See the License for the specific language governing permissions and
13# limitations under the License.
14"""The Python implementation of the GRPC helloworld.Greeter server."""
15
16import argparse
17from concurrent import futures
18import logging
19import socket
20
21import grpc
22from grpc_health.v1 import health
23from grpc_health.v1 import health_pb2
24from grpc_health.v1 import health_pb2_grpc
25from grpc_reflection.v1alpha import reflection
26import helloworld_pb2
27import helloworld_pb2_grpc
28
29_DESCRIPTION = "A general purpose phony server."
30
31_LISTEN_HOST = "0.0.0.0"
32
33_THREAD_POOL_SIZE = 256
34
35logger = logging.getLogger()
36console_handler = logging.StreamHandler()
37formatter = logging.Formatter(fmt="%(asctime)s: %(levelname)-8s %(message)s")
38console_handler.setFormatter(formatter)
39logger.addHandler(console_handler)
40
41
42class Greeter(helloworld_pb2_grpc.GreeterServicer):
43    def __init__(self, hostname: str):
44        self._hostname = hostname if hostname else socket.gethostname()
45
46    def SayHello(
47        self,
48        request: helloworld_pb2.HelloRequest,
49        context: grpc.ServicerContext,
50    ) -> helloworld_pb2.HelloReply:
51        return helloworld_pb2.HelloReply(
52            message=f"Hello {request.name} from {self._hostname}!"
53        )
54
55
56def _configure_maintenance_server(
57    server: grpc.Server, maintenance_port: int
58) -> None:
59    listen_address = f"{_LISTEN_HOST}:{maintenance_port}"
60    server.add_insecure_port(listen_address)
61
62    # Create a health check servicer. We use the non-blocking implementation
63    # to avoid thread starvation.
64    health_servicer = health.HealthServicer(
65        experimental_non_blocking=True,
66        experimental_thread_pool=futures.ThreadPoolExecutor(
67            max_workers=_THREAD_POOL_SIZE
68        ),
69    )
70
71    # Create a tuple of all of the services we want to export via reflection.
72    services = tuple(
73        service.full_name
74        for service in helloworld_pb2.DESCRIPTOR.services_by_name.values()
75    ) + (reflection.SERVICE_NAME, health.SERVICE_NAME)
76
77    # Mark all services as healthy.
78    health_pb2_grpc.add_HealthServicer_to_server(health_servicer, server)
79    for service in services:
80        health_servicer.set(service, health_pb2.HealthCheckResponse.SERVING)
81    reflection.enable_server_reflection(services, server)
82
83
84def _configure_greeter_server(
85    server: grpc.Server, port: int, secure_mode: bool, hostname
86) -> None:
87    # Add the application servicer to the server.
88    helloworld_pb2_grpc.add_GreeterServicer_to_server(Greeter(hostname), server)
89    listen_address = f"{_LISTEN_HOST}:{port}"
90    if not secure_mode:
91        server.add_insecure_port(listen_address)
92    else:
93        # Use xDS credentials.
94        logger.info("Running with xDS Server credentials")
95
96        # Fall back to insecure credentials.
97        server_fallback_creds = grpc.insecure_server_credentials()
98        server_creds = grpc.xds_server_credentials(server_fallback_creds)
99        server.add_secure_port(listen_address, server_creds)
100
101
102def serve(
103    port: int, hostname: str, maintenance_port: int, secure_mode: bool
104) -> None:
105    if port == maintenance_port:
106        # If maintenance port and port are the same, start a single server.
107        server = grpc.server(
108            futures.ThreadPoolExecutor(max_workers=_THREAD_POOL_SIZE)
109        )
110        _configure_greeter_server(server, port, secure_mode, hostname)
111        _configure_maintenance_server(server, maintenance_port)
112        server.start()
113        logger.info("Greeter server listening on port %d", port)
114        logger.info("Maintenance server listening on port %d", maintenance_port)
115        server.wait_for_termination()
116    else:
117        # Otherwise, start two different servers.
118        greeter_server = grpc.server(
119            futures.ThreadPoolExecutor(max_workers=_THREAD_POOL_SIZE),
120            xds=secure_mode,
121        )
122        _configure_greeter_server(greeter_server, port, secure_mode, hostname)
123        greeter_server.start()
124        logger.info("Greeter server listening on port %d", port)
125        maintenance_server = grpc.server(
126            futures.ThreadPoolExecutor(max_workers=_THREAD_POOL_SIZE)
127        )
128        _configure_maintenance_server(maintenance_server, maintenance_port)
129        maintenance_server.start()
130        logger.info("Maintenance server listening on port %d", maintenance_port)
131        greeter_server.wait_for_termination()
132        maintenance_server.wait_for_termination()
133
134
135if __name__ == "__main__":
136    parser = argparse.ArgumentParser(description=_DESCRIPTION)
137    parser.add_argument(
138        "port",
139        default=50051,
140        type=int,
141        nargs="?",
142        help="The port on which to listen.",
143    )
144    parser.add_argument(
145        "hostname",
146        type=str,
147        default=None,
148        nargs="?",
149        help="The name clients will see in responses.",
150    )
151    parser.add_argument(
152        "--xds-creds",
153        action="store_true",
154        help="If specified, uses xDS credentials to connect to the server.",
155    )
156    args = parser.parse_args()
157    logging.basicConfig()
158    logger.setLevel(logging.INFO)
159    serve(args.port, args.hostname, args.port + 1, args.xds_creds)
160