3

我正在 python 中的 grpc-gateway 后面编写一个 grpc 服务,如果某些用户的请求过多,我想提出 429 响应并在响应消息正文中提供验证码。

实际上我的问题是当我使用下面的代码块来提高状态码 429 时,之后我无法发送响应消息。

context.set_code(grpc.StatusCode.RESOURCE_EXHAUSTED)
context.set_details('Too many requests')
return MyServiceResponse()

据我了解,唯一的 grpc 是不可能的,但我认为第三方可能有可能。

有什么解决办法吗?

4

1 回答 1

2

一元-一元 RPC(双方非流式传输)不允许发送非 ok 状态的响应。对于流式 RPC,服务器可能会在发送错误代码之前发送响应,但不建议这样做。将正常响应与错误状态混合可能会导致未来的可维护性问题,例如,如果相同的错误适用于多个 RPC,是否所有响应 ProtoBuf 消息都应包含这些字段?

回到您的问题,“验证码”应被视为错误状态的一部分,因此可以将其添加为尾随元数据之一。-bin在您的情况下,您可以通过在尾随元数据键中添加后缀来将序列化的原始消息添加为二进制尾随元数据。

此外,还有一个官方支持的软件包grpcio-status可以为您执行此操作。

服务器端将丰富的错误状态打包到“grpc_status.status_pb2.Status”原型消息中。下面的示例仅使用常见的错误原型,但您可以将“任何”原型打包到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

于 2019-03-13T19:15:21.367 回答