Merge pull request #22460 from gnossen/python-xds-examples

Add Python Example Server with Reflection and Health Checking Enabled
reviewable/pr22117/r3^2
Richard Belleville 5 years ago committed by GitHub
commit fdee178630
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 68
      examples/python/xds/README.md
  2. 134
      examples/python/xds/helloworld_pb2.py
  3. 46
      examples/python/xds/helloworld_pb2_grpc.py
  4. 5
      examples/python/xds/requirements.txt
  5. 94
      examples/python/xds/server.py

@ -0,0 +1,68 @@
gRPC Hostname Example
=====================
The hostname example is a Hello World server whose response includes its
hostname. It also supports health and reflection services. This makes it a good
server to test infrastructure, like load balancing.
The example requires grpc to already be built. You are strongly encouraged
to check out a git release tag, since there will already be a build of gRPC
available.
### Run the example
1. Navigate to this directory:
```sh
cd grpc/examples/python/xds
```
2. Run the server
```sh
virtualenv venv -p python3
source venv/bin/activate
pip install -r requirements.txt
python server.py
```
3. Verify the Server
This step is not strictly necessary, but you can use it as a sanity check if
you'd like. If you don't have it, install
[`grpcurl`](https://github.com/fullstorydev/grpcurl/releases). This will allow
you to manually test the service.
Exercise your server's application-layer service:
```sh
> grpcurl --plaintext -d '{"name": "you"}' localhost:50051
{
"message": "Hello you from rbell.svl.corp.google.com!"
}
```
Make sure that all of your server's services are available via reflection:
```sh
> grpcurl --plaintext localhost:50051 list
grpc.health.v1.Health
grpc.reflection.v1alpha.ServerReflection
helloworld.Greeter
```
Make sure that your services are reporting healthy:
```sh
> grpcurl --plaintext -d '{"service": "helloworld.Greeter"}' localhost:50051
grpc.health.v1.Health/Check
{
"status": "SERVING"
}
> grpcurl --plaintext -d '{"service": ""}' localhost:50051
grpc.health.v1.Health/Check
{
"status": "SERVING"
}
```

@ -0,0 +1,134 @@
# Generated by the protocol buffer compiler. DO NOT EDIT!
# source: helloworld.proto
import sys
_b=sys.version_info[0]<3 and (lambda x:x) or (lambda x:x.encode('latin1'))
from google.protobuf import descriptor as _descriptor
from google.protobuf import message as _message
from google.protobuf import reflection as _reflection
from google.protobuf import symbol_database as _symbol_database
from google.protobuf import descriptor_pb2
# @@protoc_insertion_point(imports)
_sym_db = _symbol_database.Default()
DESCRIPTOR = _descriptor.FileDescriptor(
name='helloworld.proto',
package='helloworld',
syntax='proto3',
serialized_pb=_b('\n\x10helloworld.proto\x12\nhelloworld\"\x1c\n\x0cHelloRequest\x12\x0c\n\x04name\x18\x01 \x01(\t\"\x1d\n\nHelloReply\x12\x0f\n\x07message\x18\x01 \x01(\t2I\n\x07Greeter\x12>\n\x08SayHello\x12\x18.helloworld.HelloRequest\x1a\x16.helloworld.HelloReply\"\x00\x42\x36\n\x1bio.grpc.examples.helloworldB\x0fHelloWorldProtoP\x01\xa2\x02\x03HLWb\x06proto3')
)
_HELLOREQUEST = _descriptor.Descriptor(
name='HelloRequest',
full_name='helloworld.HelloRequest',
filename=None,
file=DESCRIPTOR,
containing_type=None,
fields=[
_descriptor.FieldDescriptor(
name='name', full_name='helloworld.HelloRequest.name', index=0,
number=1, type=9, cpp_type=9, label=1,
has_default_value=False, default_value=_b("").decode('utf-8'),
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
options=None),
],
extensions=[
],
nested_types=[],
enum_types=[
],
options=None,
is_extendable=False,
syntax='proto3',
extension_ranges=[],
oneofs=[
],
serialized_start=32,
serialized_end=60,
)
_HELLOREPLY = _descriptor.Descriptor(
name='HelloReply',
full_name='helloworld.HelloReply',
filename=None,
file=DESCRIPTOR,
containing_type=None,
fields=[
_descriptor.FieldDescriptor(
name='message', full_name='helloworld.HelloReply.message', index=0,
number=1, type=9, cpp_type=9, label=1,
has_default_value=False, default_value=_b("").decode('utf-8'),
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
options=None),
],
extensions=[
],
nested_types=[],
enum_types=[
],
options=None,
is_extendable=False,
syntax='proto3',
extension_ranges=[],
oneofs=[
],
serialized_start=62,
serialized_end=91,
)
DESCRIPTOR.message_types_by_name['HelloRequest'] = _HELLOREQUEST
DESCRIPTOR.message_types_by_name['HelloReply'] = _HELLOREPLY
_sym_db.RegisterFileDescriptor(DESCRIPTOR)
HelloRequest = _reflection.GeneratedProtocolMessageType('HelloRequest', (_message.Message,), dict(
DESCRIPTOR = _HELLOREQUEST,
__module__ = 'helloworld_pb2'
# @@protoc_insertion_point(class_scope:helloworld.HelloRequest)
))
_sym_db.RegisterMessage(HelloRequest)
HelloReply = _reflection.GeneratedProtocolMessageType('HelloReply', (_message.Message,), dict(
DESCRIPTOR = _HELLOREPLY,
__module__ = 'helloworld_pb2'
# @@protoc_insertion_point(class_scope:helloworld.HelloReply)
))
_sym_db.RegisterMessage(HelloReply)
DESCRIPTOR.has_options = True
DESCRIPTOR._options = _descriptor._ParseOptions(descriptor_pb2.FileOptions(), _b('\n\033io.grpc.examples.helloworldB\017HelloWorldProtoP\001\242\002\003HLW'))
_GREETER = _descriptor.ServiceDescriptor(
name='Greeter',
full_name='helloworld.Greeter',
file=DESCRIPTOR,
index=0,
options=None,
serialized_start=93,
serialized_end=166,
methods=[
_descriptor.MethodDescriptor(
name='SayHello',
full_name='helloworld.Greeter.SayHello',
index=0,
containing_service=None,
input_type=_HELLOREQUEST,
output_type=_HELLOREPLY,
options=None,
),
])
_sym_db.RegisterServiceDescriptor(_GREETER)
DESCRIPTOR.services_by_name['Greeter'] = _GREETER
# @@protoc_insertion_point(module_scope)

@ -0,0 +1,46 @@
# Generated by the gRPC Python protocol compiler plugin. DO NOT EDIT!
import grpc
import helloworld_pb2 as helloworld__pb2
class GreeterStub(object):
"""The greeting service definition.
"""
def __init__(self, channel):
"""Constructor.
Args:
channel: A grpc.Channel.
"""
self.SayHello = channel.unary_unary(
'/helloworld.Greeter/SayHello',
request_serializer=helloworld__pb2.HelloRequest.SerializeToString,
response_deserializer=helloworld__pb2.HelloReply.FromString,
)
class GreeterServicer(object):
"""The greeting service definition.
"""
def SayHello(self, request, context):
"""Sends a greeting
"""
context.set_code(grpc.StatusCode.UNIMPLEMENTED)
context.set_details('Method not implemented!')
raise NotImplementedError('Method not implemented!')
def add_GreeterServicer_to_server(servicer, server):
rpc_method_handlers = {
'SayHello': grpc.unary_unary_rpc_method_handler(
servicer.SayHello,
request_deserializer=helloworld__pb2.HelloRequest.FromString,
response_serializer=helloworld__pb2.HelloReply.SerializeToString,
),
}
generic_handler = grpc.method_handlers_generic_handler(
'helloworld.Greeter', rpc_method_handlers)
server.add_generic_rpc_handlers((generic_handler,))

@ -0,0 +1,5 @@
grpcio>=1.28.0
protobuf
grpcio-reflection
grpcio-health-checking

@ -0,0 +1,94 @@
# Copyright 2020 The gRPC authors.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""The Python implementation of the GRPC helloworld.Greeter server."""
from concurrent import futures
import argparse
import logging
import multiprocessing
import socket
import grpc
import helloworld_pb2
import helloworld_pb2_grpc
from grpc_reflection.v1alpha import reflection
from grpc_health.v1 import health
from grpc_health.v1 import health_pb2
from grpc_health.v1 import health_pb2_grpc
_DESCRIPTION = "A general purpose dummy server."
class Greeter(helloworld_pb2_grpc.GreeterServicer):
def __init__(self, hostname: str):
self._hostname = hostname if hostname else socket.gethostname()
def SayHello(self, request: helloworld_pb2.HelloRequest,
context: grpc.ServicerContext) -> helloworld_pb2.HelloReply:
return helloworld_pb2.HelloReply(
message=f"Hello {request.name} from {self._hostname}!")
def serve(port: int, hostname: str):
server = grpc.server(
futures.ThreadPoolExecutor(max_workers=multiprocessing.cpu_count()))
# Add the application servicer to the server.
helloworld_pb2_grpc.add_GreeterServicer_to_server(Greeter(hostname), server)
# Create a health check servicer. We use the non-blocking implementation
# to avoid thread starvation.
health_servicer = health.HealthServicer(
experimental_non_blocking=True,
experimental_thread_pool=futures.ThreadPoolExecutor(max_workers=1))
health_pb2_grpc.add_HealthServicer_to_server(health_servicer, server)
# Create a tuple of all of the services we want to export via reflection.
services = tuple(
service.full_name
for service in helloworld_pb2.DESCRIPTOR.services_by_name.values()) + (
reflection.SERVICE_NAME, health.SERVICE_NAME)
# Add the reflection service to the server.
reflection.enable_server_reflection(services, server)
server.add_insecure_port(f"[::]:{port}")
server.start()
# Mark all services as healthy.
overall_server_health = ""
for service in services + (overall_server_health,):
health_servicer.set(service, health_pb2.HealthCheckResponse.SERVING)
# Park the main application thread.
server.wait_for_termination()
if __name__ == '__main__':
parser = argparse.ArgumentParser(description=_DESCRIPTION)
parser.add_argument("port",
default=50051,
type=int,
nargs="?",
help="The port on which to listen.")
parser.add_argument("hostname",
type=str,
default=None,
nargs="?",
help="The name clients will see in responses.")
args = parser.parse_args()
logging.basicConfig()
serve(args.port, args.hostname)
Loading…
Cancel
Save