Recentemente, usei o gRPC para criar um serviço pela primeira vez. No lado do servidor, desejo lançar uma exceção personalizada diretamente para que o cliente possa vê-la. No começo, tentei isso:
// responseObserver.onError(new CustomException("custom exception"));
throw new CustomException("one error occurs");
Mas obteve resultados muito embaraçosos:
io.grpc.StatusRuntimeException: UNKNOWN
O cliente não pode ver a mensagem de erro que eu criei. Depois de alguma pesquisa, descobri dois tipos de clientes que podem obter informações de exceção personalizadas lançadas pelo servidor.
Método 1: definir a mensagem anormal para a descrição de status
A implementação do servidor é assim:
// 自定义异常处理
@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());
}
}
Use Status.INVALID_ARGUMENT
o código de exceção especificado, o código também é um parâmetro em questão, os parâmetros anormais do problema são principalmente do lado do serviço normal precisam ser lançados, se o serviço é o problema, então não faça tratamento especial, e ele o lança diretamente.
Chamada do cliente:
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;
}
}
Método 2: Passe informações de erro mais detalhadas por meio de MetaDados
Desta forma, um é personalizado no arquivo proto ErrorInfo
:
message ErrorInfo {
// list 里可以放很多的错误信息
repeated string message = 1;
}
O ErrorInfo definido aqui pode transportar muitas informações, por exemplo, um campo de código pode ser definido nele, e então ele pode expressar informações mais ricas.
Na classe de implementação do servidor:
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));
}
}
Em seguida, observe a chamada do cliente:
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;
}
}
Acima são todos tratamento de exceção de chamada síncrona do lado do cliente, haverá algumas pequenas diferenças no tratamento de exceção de chamada assíncrona, o código completo pode se referir a: https://github.com/jiaobuchong/grpc-learning/tree/master/grpc-error-handling
参考 :
https://github.com/grpc/grpc-java/tree/master/examples
Introdução ao gRPC
https://grpc.github.io/grpc/core/md_doc_statuscodes.html
https://stackoverflow.com/questions / 48748745 / pattern-for-rich-error-handling-in-grpc