Add retry example for gRPC Python (#26829)

* Add retry example for gRPC Python

* Make sanity test happy && simplfy proto definition

* Fix a grammer issue
pull/26837/head
Lidi Zheng 3 years ago committed by GitHub
parent ca482bdbc7
commit 6c6463e1cd
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 48
      examples/python/retry/README.md
  2. 58
      examples/python/retry/async_retry_client.py
  3. 58
      examples/python/retry/flaky_server.py
  4. 33
      examples/python/retry/helloworld.proto
  5. 56
      examples/python/retry/retry_client.py

@ -0,0 +1,48 @@
# Retry Example in gRPC Python
## Prerequisite
* grpcio >= 1.39.0
* grpcio-tools >= 1.39.0
## Running the example
In terminal 1, start the flaky server:
```sh
python3 flaky_server.py
```
In terminal 2, start the retry clients:
```sh
python3 retry_client.py
# Or
python3 async_retry_client.py
```
## Expect results
The client RPC will succeed, even with server injecting multiple errors. Here is an example server log:
```sh
$ python3 flaky_server.py
INFO:root:Starting flaky server on [::]:50051
INFO:root:Injecting error to RPC from ipv6:[::1]:54471
INFO:root:Successfully responding to RPC from ipv6:[::1]:54473
INFO:root:Injecting error to RPC from ipv6:[::1]:54491
INFO:root:Injecting error to RPC from ipv6:[::1]:54581
INFO:root:Injecting error to RPC from ipv6:[::1]:54581
INFO:root:Injecting error to RPC from ipv6:[::1]:54581
INFO:root:Injecting error to RPC from ipv6:[::1]:54581
INFO:root:Successfully responding to RPC from ipv6:[::1]:54581
INFO:root:Injecting error to RPC from ipv6:[::1]:55474
INFO:root:Injecting error to RPC from ipv6:[::1]:55474
INFO:root:Injecting error to RPC from ipv6:[::1]:55474
INFO:root:Injecting error to RPC from ipv6:[::1]:55474
INFO:root:Successfully responding to RPC from ipv6:[::1]:55474
INFO:root:Injecting error to RPC from ipv6:[::1]:55533
INFO:root:Injecting error to RPC from ipv6:[::1]:55533
INFO:root:Injecting error to RPC from ipv6:[::1]:55533
INFO:root:Successfully responding to RPC from ipv6:[::1]:55533
```

@ -0,0 +1,58 @@
# Copyright 2021 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 AsyncIO implementation of the gRPC client-side retry example."""
import asyncio
import json
import logging
import grpc
helloworld_pb2, helloworld_pb2_grpc = grpc.protos_and_services(
"helloworld.proto")
async def run() -> None:
# The ServiceConfig proto definition can be found:
# https://github.com/grpc/grpc-proto/blob/ec886024c2f7b7f597ba89d5b7d60c3f94627b17/grpc/service_config/service_config.proto#L377
service_config_json = json.dumps({
"methodConfig": [{
# To apply retry to all methods, put [{}] in the "name" field
"name": [{
"service": "helloworld.Greeter",
"method": "SayHello"
}],
"retryPolicy": {
"maxAttempts": 5,
"initialBackoff": "0.1s",
"maxBackoff": "1s",
"backoffMultiplier": 2,
"retryableStatusCodes": ["UNAVAILABLE"],
},
}]
})
options = []
# NOTE: the retry feature will be enabled by default >=v1.40.0
options.append(("grpc.enable_retries", 1))
options.append(("grpc.service_config", service_config_json))
async with grpc.aio.insecure_channel('localhost:50051',
options=options) as channel:
stub = helloworld_pb2_grpc.GreeterStub(channel)
response = await stub.SayHello(helloworld_pb2.HelloRequest(name='you'))
print("Greeter client received: " + response.message)
if __name__ == '__main__':
logging.basicConfig()
asyncio.run(run())

@ -0,0 +1,58 @@
# Copyright 2021 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.
"""A flaky backend for the gRPC Python retry example."""
import asyncio
import collections
import logging
import random
import grpc
helloworld_pb2, helloworld_pb2_grpc = grpc.protos_and_services(
"helloworld.proto")
class ErrorInjectingGreeter(helloworld_pb2_grpc.GreeterServicer):
def __init__(self):
self._counter = collections.defaultdict(int)
async def SayHello(
self, request: helloworld_pb2.HelloRequest,
context: grpc.aio.ServicerContext) -> helloworld_pb2.HelloReply:
self._counter[context.peer()] += 1
if self._counter[context.peer()] < 5:
if random.random() < 0.75:
logging.info('Injecting error to RPC from %s', context.peer())
await context.abort(grpc.StatusCode.UNAVAILABLE,
'injected error')
logging.info('Successfully responding to RPC from %s', context.peer())
return helloworld_pb2.HelloReply(message='Hello, %s!' % request.name)
async def serve() -> None:
server = grpc.aio.server()
helloworld_pb2_grpc.add_GreeterServicer_to_server(ErrorInjectingGreeter(),
server)
listen_addr = '[::]:50051'
server.add_insecure_port(listen_addr)
logging.info("Starting flaky server on %s", listen_addr)
await server.start()
await server.wait_for_termination()
if __name__ == '__main__':
logging.basicConfig(level=logging.INFO)
asyncio.run(serve())

@ -0,0 +1,33 @@
// Copyright 2021 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.
syntax = "proto3";
package helloworld;
// The greeting service definition.
service Greeter {
// Sends a greeting
rpc SayHello (HelloRequest) returns (HelloReply) {}
}
// The request message containing the user's name.
message HelloRequest {
string name = 1;
}
// The response message containing the greetings
message HelloReply {
string message = 1;
}

@ -0,0 +1,56 @@
# Copyright 2021 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 client-side retry example."""
import json
import logging
import grpc
helloworld_pb2, helloworld_pb2_grpc = grpc.protos_and_services(
"helloworld.proto")
def run():
# The ServiceConfig proto definition can be found:
# https://github.com/grpc/grpc-proto/blob/ec886024c2f7b7f597ba89d5b7d60c3f94627b17/grpc/service_config/service_config.proto#L377
service_config_json = json.dumps({
"methodConfig": [{
# To apply retry to all methods, put [{}] in the "name" field
"name": [{
"service": "helloworld.Greeter",
"method": "SayHello"
}],
"retryPolicy": {
"maxAttempts": 5,
"initialBackoff": "0.1s",
"maxBackoff": "1s",
"backoffMultiplier": 2,
"retryableStatusCodes": ["UNAVAILABLE"],
},
}]
})
options = []
# NOTE: the retry feature will be enabled by default >=v1.40.0
options.append(("grpc.enable_retries", 1))
options.append(("grpc.service_config", service_config_json))
with grpc.insecure_channel('localhost:50051', options=options) as channel:
stub = helloworld_pb2_grpc.GreeterStub(channel)
response = stub.SayHello(helloworld_pb2.HelloRequest(name='you'))
print("Greeter client received: " + response.message)
if __name__ == '__main__':
logging.basicConfig()
run()
Loading…
Cancel
Save