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