在 gRPC python asyncio 中处理客户端取消?
Handle client-side cancellation in gRPC python asyncio?
先提问,下面是上下文。我如何根据使用异步 gRPC python server?
从客户端 取消 RPC 来执行一些服务器端操作(例如,清理)
在我的微服务中,我有一个 asyncio
gRPC 服务器,其主要 RPC 是双向流。
在客户端(也使用 asyncio),当我取消某些东西时,它会引发一个 asyncio.CancelledError
,它会被 grpc
核心捕获而不是重新引发:
except asyncio.CancelledError:
_LOGGER.debug('RPC cancelled for servicer method [%s]', _decode(rpc_state.method()))
所以我不能依赖于在我自己的代码中捕获 asyncio.CancelledError
,因为它是预先捕获的并且没有重新引发。
共享上下文应该包含有关 RPC 是否在客户端被取消的信息,通过从 RPC 调用调用 .cancel()
并能够通过调用 [=19 查看它是否被取消=]:
https://grpc.github.io/grpc/python/grpc_asyncio.html#shared-context
abstract cancel()
Cancels the RPC.
Idempotent and has no effect if the RPC has already terminated.
Returns
A bool indicates if the cancellation is performed or not.
Return type
bool
abstract cancelled()
Return True if the RPC is cancelled.
The RPC is cancelled when the cancellation was requested with cancel().
Returns
A bool indicates whether the RPC is cancelled or not.
Return type
bool
但是,此共享上下文并未附加到由 gRPC 生成的代码在服务器端提供给 RPC 的 context
变量。 (我不能 运行 context.cancelled()
或 context.add_done_callback
;他们不存在)
所以,问题又来了:我如何基于从客户端取消 RPC 使用异步 gRPC python 执行一些服务器端操作(例如,清理)服务器?
感谢 post。我们已经意识到这个问题,并且在我们的路线图上添加对这两种方法的支持。
对于短期解决方案,您可以使用 try-catch 和装饰器。客户端取消在方法处理程序中被观察为 asyncio.CancelledError
。这是修改后的 helloworld example:
服务器代码:
class Greeter(helloworld_pb2_grpc.GreeterServicer):
async def SayHello(
self, request: helloworld_pb2.HelloRequest,
context: grpc.aio.ServicerContext) -> helloworld_pb2.HelloReply:
try:
await asyncio.sleep(4)
except asyncio.CancelledError:
print('RPC cancelled')
raise
return helloworld_pb2.HelloReply(message='Hello, %s!' % request.name)
客户代码:
async def run() -> None:
async with grpc.aio.insecure_channel('localhost:50051') as channel:
stub = helloworld_pb2_grpc.GreeterStub(channel)
call = stub.SayHello(helloworld_pb2.HelloRequest(name='you'))
await asyncio.sleep(2)
call.cancel()
print(await call.code())
先提问,下面是上下文。我如何根据使用异步 gRPC python server?
从客户端 取消 RPC 来执行一些服务器端操作(例如,清理)在我的微服务中,我有一个 asyncio
gRPC 服务器,其主要 RPC 是双向流。
在客户端(也使用 asyncio),当我取消某些东西时,它会引发一个 asyncio.CancelledError
,它会被 grpc
核心捕获而不是重新引发:
except asyncio.CancelledError:
_LOGGER.debug('RPC cancelled for servicer method [%s]', _decode(rpc_state.method()))
所以我不能依赖于在我自己的代码中捕获 asyncio.CancelledError
,因为它是预先捕获的并且没有重新引发。
共享上下文应该包含有关 RPC 是否在客户端被取消的信息,通过从 RPC 调用调用 .cancel()
并能够通过调用 [=19 查看它是否被取消=]:
https://grpc.github.io/grpc/python/grpc_asyncio.html#shared-context
abstract cancel()
Cancels the RPC. Idempotent and has no effect if the RPC has already terminated. Returns A bool indicates if the cancellation is performed or not. Return type bool
abstract cancelled()
Return True if the RPC is cancelled. The RPC is cancelled when the cancellation was requested with cancel(). Returns A bool indicates whether the RPC is cancelled or not. Return type bool
但是,此共享上下文并未附加到由 gRPC 生成的代码在服务器端提供给 RPC 的 context
变量。 (我不能 运行 context.cancelled()
或 context.add_done_callback
;他们不存在)
所以,问题又来了:我如何基于从客户端取消 RPC 使用异步 gRPC python 执行一些服务器端操作(例如,清理)服务器?
感谢 post。我们已经意识到这个问题,并且在我们的路线图上添加对这两种方法的支持。
对于短期解决方案,您可以使用 try-catch 和装饰器。客户端取消在方法处理程序中被观察为 asyncio.CancelledError
。这是修改后的 helloworld example:
服务器代码:
class Greeter(helloworld_pb2_grpc.GreeterServicer):
async def SayHello(
self, request: helloworld_pb2.HelloRequest,
context: grpc.aio.ServicerContext) -> helloworld_pb2.HelloReply:
try:
await asyncio.sleep(4)
except asyncio.CancelledError:
print('RPC cancelled')
raise
return helloworld_pb2.HelloReply(message='Hello, %s!' % request.name)
客户代码:
async def run() -> None:
async with grpc.aio.insecure_channel('localhost:50051') as channel:
stub = helloworld_pb2_grpc.GreeterStub(channel)
call = stub.SayHello(helloworld_pb2.HelloRequest(name='you'))
await asyncio.sleep(2)
call.cancel()
print(await call.code())