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