web-dev-qa-db-ja.com

gRPCサーバーにグローバル例外インターセプターを追加する方法

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がクライアントに渡されます。

しかし、問題は、真のエンタープライズ環境ではRuntimeExceptionsが大量に発生することであり、これらの例外のメッセージをクライアントに渡したい場合は、各メソッドをキャッチする必要があり、これは非常に面倒です。

すべてのメソッドをインターセプトし、RuntimeExceptionをキャッチしてonErrorをトリガーし、エラーメッセージをクライアントに伝播するグローバルインターセプターはありますか?サーバーコードでRuntimeExceptionsを処理する必要がないようにします。

どうもありがとう !

注意 :

<grpc.version>1.0.1</grpc.version>
com.google.protobuf:proton:3.1.0
io.grpc:protoc-gen-grpc-Java:1.0.1
23
smallufo

以下のコードは、すべてのランタイム例外をキャッチします。リンクも参照してください 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());
            }
         }
      };
   }
}
5
mahesh

TransmitStatusRuntimeExceptionInterceptor は、StatusRuntimeExceptionのみをキャッチすることを除いて、必要なものと非常に似ています。あなたはそれをフォークしてすべての例外をキャッチさせることができます。

サーバー上のすべてのサービスのインターセプターをインストールするには、gRPC 1.5.0で追加された ServerBuilder.intercept() を使用できます。

1
Kun Zhang

インターセプターの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);
}

悲しいことに、異なるスレッドコンテキストは例外処理スコープがないことを意味するため、私の答えはあなたが探しているソリューションではありません。

0
Gladmir