gRPC异常处理流程设计

我的博客阅读本文

2021-06-30新增了部分代码演示,请参考
2022-02-17更新,之前思路有局限,这里不推荐使用异常来处理业务上的异常,基于此认知,更推荐业务上能够约定固定的数据结构ResultDTO,能够描述业务上的异常,比如错误码等信息,”异常“的处理,更推荐是在不可预知的非义务异常上进行兜底,目前已在todo list中,准备重新设计,来补足业务上已知的异常处理方式。

1. 核心诉求

  • 服务提供方异常能够被服务消费方感知
  • 异常分类处理:
    1. 业务异常,需要返回对应的错误码,方便服务消费方进行国际化文案的提示+日志。
    2. 非业务异常(比如NPE),需要返回内容给到服务消费方感知。
  • 拓展性&流程尽可能简单

2. 方案选择

2.1. 直接调用OnError方法,传递Status包装异常后返回

例子:

try {
   
   

}catch (Throwable t) {
   
   
// Throwable t | StreamObserver<xxx> responseObserver
responseObserver.onError(Status.UNKNOWN
                  .withDescription(t.getMessage())
                  .withCause(t)
                  .asRuntimeException());
}

这个方式客户端可以感知到,但是可能能够放入的信息有限,只能是一个字符串,只能在withDescription这个参数里,如果要多个参数,可能借助一些序列化框架转化为字符串进行转换。

2.2. 借助protobuf 的 OneOf语法

protobuf文件:

message Request {
   
   
  int32 number = 1;
}

message SuccessResponse {
   
   
  int32 result = 1;
}

enum ErrorCode {
   
   
  ABOVE_20 = 0;
  BELOW_2 = 1;
}

message ErrorResponse {
   
   
  int32 input = 1;
  ErrorCode error_code = 2;
}

// 重点是这里,回调有两种,一个成功,一个失败
message Response {
   
   
  oneof response {
   
   
    SuccessResponse success_response = 1;
    ErrorResponse error_response = 2;
  }
}

service CalculatorService {
   
   
  rpc findSquare(Request) returns (Response) {
   
   };
}

Java代码:

@Override
public void findSquare(Request request, StreamObserver<Response> responseObserver) {
   
   
    Response.Builder builder = Response.newBuilder();
		try {
   
   
			// 异常业务
		}catch (Throwable t) {
   
   
				// 有异常的话返回错误的Message类型
        ErrorResponse errorResponse = ErrorResponse.newBuilder()
								// 业务异常
                .setInput(xxx)
								// 业务错误代码
                .setErrorCode(errorCode)
                .build();
        builder.setErrorResponse(errorResponse);
				return;
		}
		
		// 成功的话返回正确的Message类型
### gRPC 中的超时和异常处理 #### 处理超时 在 Java 和 Python 实现中,gRPC 提供了设置请求截止时间(deadline)的方法来管理超时。当客户端发起带有 deadline 的 RPC 请求时,如果服务端未能在此时间内完成响应,则该请求会被取消并抛出 `DEADLINE_EXCEEDED` 错误。 Java 客户端可以通过 `withDeadlineAfter()` 或者 `withDeadline()` 来指定一个相对或绝对的时间点作为 deadline: ```java // 设置5秒后的deadline stub.withDeadlineAfter(5, TimeUnit.SECONDS).yourRpcMethod(request); ``` Python 客户端同样支持通过参数传递 timeout 值给 RPC 方法调用来实现相同功能[^2]: ```python # 使用 with_call() 并传入timeout=5表示等待最多五秒钟 response, call = stub.YourUnaryCall(your_request_message, timeout=5) ``` #### 异常处理机制 对于可能出现的各种类型的错误状况,gRPC 设计了一套标准的状态码体系用于描述不同的失败原因。这些状态码可以在客户端和服务端之间传播,并允许开发者基于特定的状态码执行相应的逻辑操作。 以 Java 为例,在遇到取消的情况时,代码片段展示了如何检测来自上下文(Context)对象中的取消信号,并据此作出适当反应[^1]: ```java if (Context.current().isCancelled()) { responseObserver.onError( Status.CANCELLED.withDescription("Cancelled by client") .asRuntimeException()); return; } ``` 而在 Python 中,通常会在 try-except 结构内捕获由远程过程调用引发的异常实例(`_Rendezvous`),进而判断其内部属性如 code 和 details 进行进一步处理: ```python try: response = stub.YourStreamCall(your_request_iterator, metadata=(('key', 'value'),)) except grpc.RpcError as e: status_code = e.code() message = e.details() if status_code == grpc.StatusCode.DEADLINE_EXCEEDED: print(f"Request timed out: {message}") elif status_code == grpc.StatusCode.UNAVAILABLE: print(f"Service unavailable: {message}") else: raise ``` 此外,关于何时以及怎样重试失败的 RPC 请求,这取决于具体应用场景的需求。由于不同情况下可能会产生相同的 gRPC 状态码,所以应用程序应当定义自己的策略来决定哪些状态值得被重试[^4]。
评论 13
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值