mirror of https://github.com/grpc/grpc.git
Merge pull request #24755 from lidizheng/aio-examples
Polish and add AsyncIO examplespull/22334/head^2
commit
befc7a7d4b
13 changed files with 374 additions and 35 deletions
@ -0,0 +1,101 @@ |
||||
# 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. |
||||
"""Client of the Python AsyncIO example of customizing authentication mechanism.""" |
||||
|
||||
import argparse |
||||
import asyncio |
||||
import logging |
||||
|
||||
import grpc |
||||
|
||||
import _credentials |
||||
|
||||
helloworld_pb2, helloworld_pb2_grpc = grpc.protos_and_services( |
||||
"helloworld.proto") |
||||
|
||||
_LOGGER = logging.getLogger(__name__) |
||||
_LOGGER.setLevel(logging.INFO) |
||||
|
||||
_SERVER_ADDR_TEMPLATE = 'localhost:%d' |
||||
_SIGNATURE_HEADER_KEY = 'x-signature' |
||||
|
||||
|
||||
class AuthGateway(grpc.AuthMetadataPlugin): |
||||
|
||||
def __call__(self, context: grpc.AuthMetadataContext, |
||||
callback: grpc.AuthMetadataPluginCallback) -> None: |
||||
"""Implements authentication by passing metadata to a callback. |
||||
|
||||
Implementations of this method must not block. |
||||
|
||||
Args: |
||||
context: An AuthMetadataContext providing information on the RPC that |
||||
the plugin is being called to authenticate. |
||||
callback: An AuthMetadataPluginCallback to be invoked either |
||||
synchronously or asynchronously. |
||||
""" |
||||
# Example AuthMetadataContext object: |
||||
# AuthMetadataContext( |
||||
# service_url=u'https://localhost:50051/helloworld.Greeter', |
||||
# method_name=u'SayHello') |
||||
signature = context.method_name[::-1] |
||||
callback(((_SIGNATURE_HEADER_KEY, signature),), None) |
||||
|
||||
|
||||
def create_client_channel(addr: str) -> grpc.aio.Channel: |
||||
# Call credential object will be invoked for every single RPC |
||||
call_credentials = grpc.metadata_call_credentials(AuthGateway(), |
||||
name='auth gateway') |
||||
# Channel credential will be valid for the entire channel |
||||
channel_credential = grpc.ssl_channel_credentials( |
||||
_credentials.ROOT_CERTIFICATE) |
||||
# Combining channel credentials and call credentials together |
||||
composite_credentials = grpc.composite_channel_credentials( |
||||
channel_credential, |
||||
call_credentials, |
||||
) |
||||
channel = grpc.aio.secure_channel(addr, composite_credentials) |
||||
return channel |
||||
|
||||
|
||||
async def send_rpc(channel: grpc.aio.Channel) -> helloworld_pb2.HelloReply: |
||||
stub = helloworld_pb2_grpc.GreeterStub(channel) |
||||
request = helloworld_pb2.HelloRequest(name='you') |
||||
try: |
||||
response = await stub.SayHello(request) |
||||
except grpc.RpcError as rpc_error: |
||||
_LOGGER.error('Received error: %s', rpc_error) |
||||
return rpc_error |
||||
else: |
||||
_LOGGER.info('Received message: %s', response) |
||||
return response |
||||
|
||||
|
||||
async def main() -> None: |
||||
parser = argparse.ArgumentParser() |
||||
parser.add_argument('--port', |
||||
nargs='?', |
||||
type=int, |
||||
default=50051, |
||||
help='the address of server') |
||||
args = parser.parse_args() |
||||
|
||||
channel = create_client_channel(_SERVER_ADDR_TEMPLATE % args.port) |
||||
await send_rpc(channel) |
||||
await channel.close() |
||||
|
||||
|
||||
if __name__ == '__main__': |
||||
logging.basicConfig(level=logging.INFO) |
||||
asyncio.run(main()) |
@ -0,0 +1,103 @@ |
||||
# 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. |
||||
"""Server of the Python AsyncIO example of customizing authentication mechanism.""" |
||||
|
||||
import argparse |
||||
import asyncio |
||||
import logging |
||||
from typing import Awaitable, Callable, Tuple |
||||
|
||||
import grpc |
||||
|
||||
import _credentials |
||||
|
||||
helloworld_pb2, helloworld_pb2_grpc = grpc.protos_and_services( |
||||
"helloworld.proto") |
||||
|
||||
_LOGGER = logging.getLogger(__name__) |
||||
_LOGGER.setLevel(logging.INFO) |
||||
|
||||
_LISTEN_ADDRESS_TEMPLATE = 'localhost:%d' |
||||
_SIGNATURE_HEADER_KEY = 'x-signature' |
||||
|
||||
|
||||
class SignatureValidationInterceptor(grpc.aio.ServerInterceptor): |
||||
|
||||
def __init__(self): |
||||
|
||||
def abort(ignored_request, context: grpc.aio.ServicerContext) -> None: |
||||
context.abort(grpc.StatusCode.UNAUTHENTICATED, 'Invalid signature') |
||||
|
||||
self._abort_handler = grpc.unary_unary_rpc_method_handler(abort) |
||||
|
||||
async def intercept_service( |
||||
self, continuation: Callable[[grpc.HandlerCallDetails], Awaitable[ |
||||
grpc.RpcMethodHandler]], |
||||
handler_call_details: grpc.HandlerCallDetails |
||||
) -> grpc.RpcMethodHandler: |
||||
# Example HandlerCallDetails object: |
||||
# _HandlerCallDetails( |
||||
# method=u'/helloworld.Greeter/SayHello', |
||||
# invocation_metadata=...) |
||||
method_name = handler_call_details.method.split('/')[-1] |
||||
expected_metadata = (_SIGNATURE_HEADER_KEY, method_name[::-1]) |
||||
if expected_metadata in handler_call_details.invocation_metadata: |
||||
return await continuation(handler_call_details) |
||||
else: |
||||
return self._abort_handler |
||||
|
||||
|
||||
class SimpleGreeter(helloworld_pb2_grpc.GreeterServicer): |
||||
|
||||
async def SayHello(self, request: helloworld_pb2.HelloRequest, |
||||
unused_context) -> helloworld_pb2.HelloReply: |
||||
return helloworld_pb2.HelloReply(message='Hello, %s!' % request.name) |
||||
|
||||
|
||||
async def run_server(port: int) -> Tuple[grpc.aio.Server, int]: |
||||
# Bind interceptor to server |
||||
server = grpc.aio.server(interceptors=(SignatureValidationInterceptor(),)) |
||||
helloworld_pb2_grpc.add_GreeterServicer_to_server(SimpleGreeter(), server) |
||||
|
||||
# Loading credentials |
||||
server_credentials = grpc.ssl_server_credentials((( |
||||
_credentials.SERVER_CERTIFICATE_KEY, |
||||
_credentials.SERVER_CERTIFICATE, |
||||
),)) |
||||
|
||||
# Pass down credentials |
||||
port = server.add_secure_port(_LISTEN_ADDRESS_TEMPLATE % port, |
||||
server_credentials) |
||||
|
||||
await server.start() |
||||
return server, port |
||||
|
||||
|
||||
async def main() -> None: |
||||
parser = argparse.ArgumentParser() |
||||
parser.add_argument('--port', |
||||
nargs='?', |
||||
type=int, |
||||
default=50051, |
||||
help='the listening port') |
||||
args = parser.parse_args() |
||||
|
||||
server, port = await run_server(args.port) |
||||
logging.info('Server is listening at port :%d', port) |
||||
await server.wait_for_termination() |
||||
|
||||
|
||||
if __name__ == '__main__': |
||||
logging.basicConfig(level=logging.INFO) |
||||
asyncio.run(main()) |
@ -0,0 +1 @@ |
||||
../../protos/helloworld.proto |
@ -0,0 +1,43 @@ |
||||
# 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. |
||||
"""gRPC Python AsyncIO helloworld.Greeter client with channel options and timeout parameters.""" |
||||
|
||||
import asyncio |
||||
import logging |
||||
|
||||
import grpc |
||||
|
||||
import helloworld_pb2 |
||||
import helloworld_pb2_grpc |
||||
|
||||
# For more channel options, please see https://grpc.io/grpc/core/group__grpc__arg__keys.html |
||||
CHANNEL_OPTIONS = [('grpc.lb_policy_name', 'pick_first'), |
||||
('grpc.enable_retries', 0), |
||||
('grpc.keepalive_timeout_ms', 10000)] |
||||
|
||||
|
||||
async def run() -> None: |
||||
async with grpc.aio.insecure_channel(target='localhost:50051', |
||||
options=CHANNEL_OPTIONS) as channel: |
||||
stub = helloworld_pb2_grpc.GreeterStub(channel) |
||||
# Timeout in seconds. |
||||
# Please refer gRPC Python documents for more detail. https://grpc.io/grpc/python/grpc.html |
||||
response = await stub.SayHello(helloworld_pb2.HelloRequest(name='you'), |
||||
timeout=10) |
||||
print("Greeter client received: " + response.message) |
||||
|
||||
|
||||
if __name__ == '__main__': |
||||
logging.basicConfig() |
||||
asyncio.run(run()) |
@ -0,0 +1,49 @@ |
||||
# 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 reflection-enabled version of gRPC AsyncIO helloworld.Greeter server.""" |
||||
|
||||
import asyncio |
||||
import logging |
||||
|
||||
import grpc |
||||
from grpc_reflection.v1alpha import reflection |
||||
|
||||
import helloworld_pb2 |
||||
import helloworld_pb2_grpc |
||||
|
||||
|
||||
class Greeter(helloworld_pb2_grpc.GreeterServicer): |
||||
|
||||
async def SayHello(self, request: helloworld_pb2.HelloRequest, |
||||
context: grpc.aio.ServicerContext |
||||
) -> helloworld_pb2.HelloReply: |
||||
return helloworld_pb2.HelloReply(message='Hello, %s!' % request.name) |
||||
|
||||
|
||||
async def serve() -> None: |
||||
server = grpc.aio.server() |
||||
helloworld_pb2_grpc.add_GreeterServicer_to_server(Greeter(), server) |
||||
SERVICE_NAMES = ( |
||||
helloworld_pb2.DESCRIPTOR.services_by_name['Greeter'].full_name, |
||||
reflection.SERVICE_NAME, |
||||
) |
||||
reflection.enable_server_reflection(SERVICE_NAMES, server) |
||||
server.add_insecure_port('[::]:50051') |
||||
await server.start() |
||||
await server.wait_for_termination() |
||||
|
||||
|
||||
if __name__ == '__main__': |
||||
logging.basicConfig() |
||||
asyncio.run(serve()) |
Loading…
Reference in new issue