GRPCで、RuntimeException
をインターセプトし、意味のある情報をクライアントに伝達するグローバル例外インターセプターを追加するにはどうすればよいですか?
たとえば、divide
メソッドは、/ by zero
メッセージとともにArithmeticException
をスローする場合があります。サーバー側では、私は書くかもしれません:
@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();
}
クライアントがdenominator = 0を渡すと、次のようになります。
Exception in thread "main" io.grpc.StatusRuntimeException: UNKNOWN
そしてサーバー出力
Exception while executing runnable io.grpc.internal.ServerImpl$JumpToApplicationThreadServerStreamListener$2@62e95ade
Java.lang.ArithmeticException: / by zero
クライアントは何が起こっているのか知りません。
/ by zero
メッセージをクライアントに渡したい場合は、サーバーを次のように変更する必要があります(これについて説明します question )
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())));
}
また、クライアントが分母= 0を送信すると、次のようになります。
Exception in thread "main" io.grpc.StatusRuntimeException: INTERNAL: / by zero
良い、/ by zero
がクライアントに渡されます。
しかし、問題は、真のエンタープライズ環境ではRuntimeException
sが大量に発生することであり、これらの例外のメッセージをクライアントに渡したい場合は、各メソッドをキャッチする必要があり、これは非常に面倒です。
すべてのメソッドをインターセプトし、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
以下のコードは、すべてのランタイム例外をキャッチします。リンクも参照してください 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());
}
}
};
}
}
TransmitStatusRuntimeExceptionInterceptor は、StatusRuntimeException
のみをキャッチすることを除いて、必要なものと非常に似ています。あなたはそれをフォークしてすべての例外をキャッチさせることができます。
サーバー上のすべてのサービスのインターセプターをインストールするには、gRPC 1.5.0で追加された ServerBuilder.intercept()
を使用できます。
インターセプターのgrpc Java examples を読みましたか?
したがって、私の場合、サーバーがクライアントに送信したエラーの種類を定義するための標準としてコードとメッセージを使用します。
例:サーバーは次のような応答を送信します
{
code: 409,
message: 'Id xxx aldready exist'
}
したがって、クライアントでは、そのコードを取得してReflectionで応答するためのクライアントインターセプターをセットアップできます。 Fyiは grpcのLognet Spring Bootスターター をサーバーとして使用し、Springブートをクライアントとして使用します。
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;
}
}
上記のサンプルインターセプター。
bootstrapもちろん、それから何かを期待する前にそれをする必要があります。
serverBuilder.addService (ServerInterceptors.intercept (bindableService, interceptor));
[〜#〜]更新[〜#〜]
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);
}
悲しいことに、異なるスレッドコンテキストは例外処理スコープがないことを意味するため、私の答えはあなたが探しているソリューションではありません。