我正在python中的grpc-gateway后面编写一个grpc服务,如果某些用户的请求太多,我想提出429个响应,并在响应消息的正文中提供验证码。
实际上,我的问题是,当我使用下面的代码块引发状态代码429之后,我无法发送响应消息。
context.set_code(grpc.StatusCode.RESOURCE_EXHAUSTED)
context.set_details('Too many requests')
return MyServiceResponse()
据我了解,only-grpc是不可能的,但是我认为第三方可能会实现。
对此有什么解决办法吗?
答案 0 :(得分:1)
一元一元RPC不允许发送非正常状态的响应(双方均不流式传输)。对于流式RPC,服务器可以在发送错误代码之前先发送响应,但不建议这样做。将正常响应与错误状态混合在一起可能会导致将来的可维护性问题,例如如果同一错误适用于多个RPC,则所有响应ProtoBuf消息都应包括这些字段吗?
回到您的问题,“验证码令牌”应被视为错误状态的一部分,因此可以将其添加为尾随的元数据之一。对于您自己的情况,可以通过在尾随的元数据密钥中添加一个-bin
后缀来将序列化的原始消息作为二进制尾随的元数据添加。
此外,还有一个官方支持的软件包grpcio-status
为您完成此任务。
服务器端将丰富的错误状态打包到“ grpc_status.status_pb2.Status”原始消息中。下面的示例仅使用常见的错误原型,但是您可以将“ any”原型打包到details
中,只要您的客户理解它们即可。
# Server side
from grpc_status import rpc_status
from google.protobuf import any_pb2
def ...Servicer(...):
def AnRPCCall(request, context):
...
detail = any_pb2.Any()
detail.Pack(
rpc_status.error_details_pb2.DebugInfo(
stack_entries=traceback.format_stack(),
detail="Can't recognize this argument",
)
)
rich_status = grpc_status.status_pb2.Status(
code=grpc_status.code_pb2.INVALID_ARGUMENT,
message='Invalid argument',
details=[detail]
)
context.abort_with_status(rpc_status.to_status(rich_status))
# The method handler will abort
客户端解码错误,并对错误做出反应。
# Client side
try:
self._channel.unary_unary(_ERROR_DETAILS).with_call(_REQUEST)
except grpc.RpcError as rpc_error:
status = rpc_status.from_call(rpc_error)
for detail in status.details:
if detail.Is(error_details_pb2.DebugInfo.DESCRIPTOR):
info = error_details_pb2.DebugInfo()
detail.Unpack(info)
# Handle the debug info
elif detail.Is(OtherErrorProto.DESCRIPTOR):
# Handle the other error proto
else:
# Handle unknown error
详细了解丰富状态:https://github.com/grpc/proposal/blob/master/L44-python-rich-status.md