最近、gRPCを使用してサービスを初めて作成しました。サーバー側では、クライアントが表示できるように、カスタム例外を直接スローしたいと思います。最初に、私はこれを試しました:
// responseObserver.onError(new CustomException("custom exception"));
throw new CustomException("one error occurs");
しかし、非常に恥ずかしい結果が得られました。
io.grpc.StatusRuntimeException: UNKNOWN
クライアントは、私がカスタムスローしたエラーメッセージを見ることができません。調査の結果、サーバーからスローされたカスタム例外情報を取得できる2種類のクライアントが見つかりました。
方法1:異常メッセージをステータスの説明に設定します
サーバーの実装は次のようになります。
// 自定义异常处理
@Override
public void customException(EchoRequest request, StreamObserver<EchoResponse> responseObserver) {
try {
if (request.getMessage().equals("error")) {
throw new CustomException("custom exception message");
}
EchoResponse echoResponse = EchoResponse.newBuilder().build();
responseObserver.onNext(echoResponse);
responseObserver.onCompleted();
} catch (CustomException e) {
responseObserver.onError(Status.INVALID_ARGUMENT
// 这里就是我们的自定义异常信息
.withDescription(e.getMessage())
.withCause(e)
.asRuntimeException());
}
}
Status.INVALID_ARGUMENT
指定された例外コードを使用します。コードも問題のパラメーターです。問題の異常なパラメーターはほとんど正常です。サービス側は明確にスローする必要があります。サービスに問題がある場合は、特別な処理を行わずに直接スローします。
クライアントコール:
try {
EchoResponse echoResponse = stub.customException(
EchoRequest.newBuilder().setMessage("error").build());
System.out.println(echoResponse.getMessage());
} catch (StatusRuntimeException e) {
e.printStackTrace();
// INVALID_ARGUMENT: occurs exception
// 这个message 会包含 INVALID_ARGUMENT, 不是我们想需要的
System.out.println(e.getMessage());
if (e.getStatus().getCode() == Status.Code.INVALID_ARGUMENT) {
// 这就是我们想要的自定义异常的信息
System.out.println(e.getStatus().getDescription());
// 抛出 CustomException, 方便我们的 ExceptionHandler 处理
throw new CustomException(e.getStatus().getDescription());
} else {
throw e;
}
}
方法2:MetaDataを介してより詳細なエラー情報を渡す
このようにして、protoファイルでカスタマイズされますErrorInfo
。
message ErrorInfo {
// list 里可以放很多的错误信息
repeated string message = 1;
}
ここで定義されたErrorInfoは、多くの情報を運ぶことができます。たとえば、コードフィールドを定義して、より豊富な情報を表現することができます。
サーバー実装クラス:
private static final Metadata.Key<ErrorInfo> ERROR_INFO_TRAILER_KEY =
ProtoUtils.keyForProto(ErrorInfo.getDefaultInstance());
@Override
public void detailErrorMessage(EchoRequest request, StreamObserver<EchoResponse> responseObserver) {
try {
if (request.getMessage().equals("error")) {
throw new CustomException("custom exception message");
}
EchoResponse echoResponse = EchoResponse.newBuilder().build();
responseObserver.onNext(echoResponse);
responseObserver.onCompleted();
} catch (CustomException e) {
Metadata trailers = new Metadata();
ErrorInfo.Builder builder = ErrorInfo.newBuilder()
.addMessage(e.getMessage());
trailers.put(ERROR_INFO_TRAILER_KEY, builder.build());
responseObserver.onError(Status.INVALID_ARGUMENT
.withCause(e)
.asRuntimeException(trailers));
}
}
次に、クライアント呼び出しを見てください。
try {
EchoResponse echoResponse = stub.detailErrorMessage(
EchoRequest.newBuilder().setMessage("error").build());
System.out.println(echoResponse.getMessage());
} catch (StatusRuntimeException e) {
if (e.getStatus().getCode() == Status.Code.INVALID_ARGUMENT) {
Metadata trailers = Status.trailersFromThrowable(e);
if (trailers.containsKey(ERROR_INFO_TRAILER_KEY)) {
ErrorInfo errorInfo = trailers.get(ERROR_INFO_TRAILER_KEY);
if (errorInfo.getMessageList() != null && errorInfo.getMessageList().size() != 0) {
// 这就是我们想要的自定义异常的信息
System.out.println(errorInfo.getMessageList());
}
}
} else {
throw e;
}
}
上記はすべてクライアント側の同期呼び出しの例外処理です。非同期呼び出しの例外処理にはいくつかの小さな違いがあります。完全なコードは次を参照できます:https://github.com/jiaobuchong/grpc-learning/tree/master/grpc-error-handling
参考:
https://github.com/grpc/grpc-java/tree/master/examples
gRPC入門
https://grpc.github.io/grpc/core/md_doc_statuscodes.html
https://stackoverflow.com/questions / 48748745 / pattern-for-rich-error-handling-in-grpc