java 如何在 gRPC 服务器中添加全局异常拦截器?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/39797142/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How to add global exception interceptor in gRPC server?
提问by smallufo
In gRPC , how to add a global exception interceptor that intercepts any RuntimeException
and propagate meaningful information to the client ?
在 gRPC 中,如何添加一个全局异常拦截器来拦截任何RuntimeException
并向客户端传播有意义的信息?
for example , a divide
method may throw ArithmeticException
with / by zero
message . In the server side , I may write :
例如,一个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();
}
If the client passes denominator = 0 , it will get :
如果客户端通过 denominator = 0 ,它将得到:
Exception in thread "main" io.grpc.StatusRuntimeException: UNKNOWN
And the server outputs
并且服务器输出
Exception while executing runnable io.grpc.internal.ServerImpl$JumpToApplicationThreadServerStreamListener@62e95ade
java.lang.ArithmeticException: / by zero
The client doesn't know what's going on.
客户不知道发生了什么。
If I want to pass / by zero
message to client , I have to modify server to :
(as described in this question)
如果我想将/ 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())));
}
And if client sends denominator = 0 , it will get :
如果客户端发送 denominator = 0 ,它将得到:
Exception in thread "main" io.grpc.StatusRuntimeException: INTERNAL: / by zero
Good , / by zero
is passed to client.
好,/ by zero
传给客户端。
But the problem is , in a truly enterprise environment, there will be a lot of RuntimeException
s , and if I want to pass these exception's messages to client , I will have to try catch each method , which is very cumbersome.
但是问题是,在真正的企业环境中,会有很多的RuntimeException
s,如果我想把这些异常的消息传递给客户端,我必须尝试catch每个方法,非常麻烦。
Is there any global interceptor that intercepts every method , catching RuntimeException
and trigger onError
and propagate the error message to client ? So that I don't have to deal with RuntimeException
s in my server code .
是否有任何全局拦截器可以拦截每个方法,捕获RuntimeException
并触发onError
并将错误消息传播到客户端?这样我就不必RuntimeException
在我的服务器代码中处理s 了。
Thanks a lot !
非常感谢 !
Note :
笔记 :
<grpc.version>1.0.1</grpc.version>
com.google.protobuf:proton:3.1.0
io.grpc:protoc-gen-grpc-java:1.0.1
回答by mahesh
Below code will catch all runtime exceptions, Also refer the link https://github.com/grpc/grpc-java/issues/1552
下面的代码将捕获所有运行时异常,另请参阅链接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());
}
}
};
}
}
回答by Kun Zhang
TransmitStatusRuntimeExceptionInterceptoris very similar to what you want, except that it only catches StatusRuntimeException
. You can fork it and make it catch all exceptions.
TransmitStatusRuntimeExceptionInterceptor与您想要的非常相似,只是它只捕获StatusRuntimeException
. 您可以分叉它并使其捕获所有异常。
To install an interceptor for all services on a server, you can use ServerBuilder.intercept()
, which was added in gRPC 1.5.0
要为服务器上的所有服务安装拦截器,您可以使用ServerBuilder.intercept()
gRPC 1.5.0 中添加的
回答by Reza Adha Hamonangan
Have you read the grpc java examplesfor interceptor?
您是否阅读了拦截器的 grpc java示例?
So in my case, we use code and message as standard for defining what kind of error the server sent onto client.
所以在我的例子中,我们使用代码和消息作为标准来定义服务器发送到客户端的错误类型。
Examples: server send response like
示例:服务器发送响应如
{
code: 409,
message: 'Id xxx aldready exist'
}
So in the client you can setup client interceptor for getting that code and response with Reflection. Fyi we use Lognet Spring Boot starter for grpcas the server, and Spring boot for the client.
因此,在客户端中,您可以设置客户端拦截器以使用反射获取该代码和响应。仅供参考,我们使用Lognet Spring Boot starter for grpc作为服务器,使用 Spring boot 作为客户端。
回答by Gladmir
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;
}
}
}
Sample interceptor above.
上面的示例拦截器。
You need to bootstrap it, of course, before expecting anything from it;
当然,您需要先引导它,然后才能从中获得任何东西;
serverBuilder.addService (ServerInterceptors.intercept (bindableService, interceptor));
UPDATE
更新
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);
}
Sadly, different thread context means no exception handling scope, so my answer is not the solution you are looking..
可悲的是,不同的线程上下文意味着没有异常处理范围,所以我的答案不是您正在寻找的解决方案。
回答by sully
I have used AOP to deal with rpc errors globally, and I find it convenient. I use the AOP in guice, and the way to use it in spring should be similar
我已经使用AOP全局处理rpc错误,我觉得很方便。我在guice中使用了AOP,spring的使用方式应该是类似的
- define a method interceptor
- 定义方法拦截器
```
``
public class ServerExceptionInterceptor implements MethodInterceptor {
final static Logger logger = LoggerFactory.getLogger(ServerExceptionInterceptor.class);
public Object invoke(MethodInvocation invocation) throws Throwable {
try {
return invocation.proceed();
} catch (Exception ex) {
String stackTrace = Throwables.getStackTraceAsString(ex);
logger.error("##grpc server side error, {}", stackTrace);
Object[] args = invocation.getArguments();
StreamObserver<?> responseObserver = (StreamObserver<?>)args[1];
responseObserver.onError(Status.INTERNAL
.withDescription(stackTrace)
.withCause(ex)
.asRuntimeException());
return null;
}
}
@Target({ElementType.METHOD, ElementType.TYPE}) @Retention(RUNTIME)
public @interface WrapError {
String value() default "";
}
}
```
``
add @WrapError to all rpc method
@Override @WrapError public void sayHello(HelloRequest req, StreamObserver<HelloReply> responseObserver) { HelloReply reply = HelloReply.newBuilder().setMessage("Hello " + req.getName()).build(); logger.info("#rpc server, sayHello, planId: {}", req.getName()); if(true) throw new RuntimeException("testing-rpc-error"); //simulate an exception responseObserver.onNext(reply); responseObserver.onCompleted(); }
- bind the interceptor in guice module
将@WrapError 添加到所有 rpc 方法
@Override @WrapError public void sayHello(HelloRequest req, StreamObserver<HelloReply> responseObserver) { HelloReply reply = HelloReply.newBuilder().setMessage("Hello " + req.getName()).build(); logger.info("#rpc server, sayHello, planId: {}", req.getName()); if(true) throw new RuntimeException("testing-rpc-error"); //simulate an exception responseObserver.onNext(reply); responseObserver.onCompleted(); }
- 在 guice 模块中绑定拦截器
ServerExceptionInterceptor interceptor = new ServerExceptionInterceptor();
requestInjection(interceptor);
bindInterceptor(Matchers.any(), Matchers.annotatedWith(WrapError.class), interceptor);
ServerExceptionInterceptor interceptor = new ServerExceptionInterceptor();
requestInjection(interceptor);
bindInterceptor(Matchers.any(), Matchers.annotatedWith(WrapError.class), interceptor);
4.testing
4.测试