sma*_*ufo 21 java protocol-buffers grpc grpc-java
在gRPC中,如何添加拦截任何RuntimeException并将有意义的信息传播给客户端的全局异常拦截器?
例如,一个divide方法可能引发ArithmeticException与/ by zero消息.在服务器端,我可以写:
@Override
public void divide(DivideRequest request, StreamObserver<DivideResponse> responseObserver) {
int dom = request.getDenominator();
int num = request.getNumerator();
double result = num / dom;
responseObserver.onNext(DivideResponse.newBuilder().setValue(result).build());
responseObserver.onCompleted();
}
Run Code Online (Sandbox Code Playgroud)
如果客户端通过denominator = 0,它将得到:
Exception in thread "main" io.grpc.StatusRuntimeException: UNKNOWN
Run Code Online (Sandbox Code Playgroud)
并且服务器输出
Exception while executing runnable io.grpc.internal.ServerImpl$JumpToApplicationThreadServerStreamListener$2@62e95ade
java.lang.ArithmeticException: / by zero
Run Code Online (Sandbox Code Playgroud)
客户不知道发生了什么.
如果我想将/ by zero消息传递给客户端,我必须将服务器修改为:(如本问题所述)
try {
double result = num / dom;
responseObserver.onNext(DivideResponse.newBuilder().setValue(result).build());
responseObserver.onCompleted();
} catch (Exception e) {
logger.error("onError : {}" , e.getMessage());
responseObserver.onError(new StatusRuntimeException(Status.INTERNAL.withDescription(e.getMessage())));
}
Run Code Online (Sandbox Code Playgroud)
如果客户端发送denominator = 0,它将得到:
Exception in thread "main" io.grpc.StatusRuntimeException: INTERNAL: / by zero
Run Code Online (Sandbox Code Playgroud)
很好,/ by zero传递给客户.
但问题是,在真正的企业环境中,会有很多RuntimeExceptions,如果我想将这些异常的消息传递给客户端,我将不得不尝试捕获每个方法,这非常麻烦.
是否有任何全局拦截器拦截每个方法,捕获RuntimeException并触发onError并将错误消息传播给客户端?这样我就不必RuntimeException在服务器代码中处理s了.
非常感谢 !
注意 :
<grpc.version>1.0.1</grpc.version>
com.google.protobuf:proton:3.1.0
io.grpc:protoc-gen-grpc-java:1.0.1
Run Code Online (Sandbox Code Playgroud)
小智 7
下面的代码将捕获所有运行时异常,另请参阅链接https://github.com/grpc/grpc-java/issues/1552
public class GlobalGrpcExceptionHandler implements ServerInterceptor {
@Override
public <ReqT, RespT> ServerCall.Listener<ReqT> interceptCall(ServerCall<ReqT, RespT> call,
Metadata requestHeaders, ServerCallHandler<ReqT, RespT> next) {
ServerCall.Listener<ReqT> delegate = next.startCall(call, requestHeaders);
return new SimpleForwardingServerCallListener<ReqT>(delegate) {
@Override
public void onHalfClose() {
try {
super.onHalfClose();
} catch (Exception e) {
call.close(Status.INTERNAL
.withCause (e)
.withDescription("error message"), new Metadata());
}
}
};
}
}
Run Code Online (Sandbox Code Playgroud)
public class GrpcExceptionHandler implements ServerInterceptor {
private final Logger logger = LoggerFactory.getLogger (GrpcExceptionHandler.class);
@Override
public <ReqT, RespT> ServerCall.Listener<ReqT> interceptCall (ServerCall<ReqT, RespT> call,
Metadata headers,
ServerCallHandler<ReqT, RespT> next) {
logger.info ("GRPC call at: {}", Instant.now ());
ServerCall.Listener<ReqT> listener;
try {
listener = next.startCall (call, headers);
} catch (Throwable ex) {
logger.error ("Uncaught exception from grpc service");
call.close (Status.INTERNAL
.withCause (ex)
.withDescription ("Uncaught exception from grpc service"), null);
return new ServerCall.Listener<ReqT>() {};
}
return listener;
}
Run Code Online (Sandbox Code Playgroud)
}
上面的示例拦截器。
当然,在期待它产生任何结果之前,您需要先引导它;
serverBuilder.addService (ServerInterceptors.intercept (bindableService, interceptor));
Run Code Online (Sandbox Code Playgroud)
更新
public interface ServerCallHandler<RequestT, ResponseT> {
/**
* Produce a non-{@code null} listener for the incoming call. Implementations are free to call
* methods on {@code call} before this method has returned.
*
* <p>If the implementation throws an exception, {@code call} will be closed with an error.
* Implementations must not throw an exception if they started processing that may use {@code
* call} on another thread.
*
* @param call object for responding to the remote client.
* @return listener for processing incoming request messages for {@code call}
*/
ServerCall.Listener<RequestT> startCall(
ServerCall<RequestT, ResponseT> call,
Metadata headers);
}
Run Code Online (Sandbox Code Playgroud)
可悲的是,不同的线程上下文意味着没有异常处理范围,所以我的答案不是您正在寻找的解决方案。