Tratamento de exceções gRPC

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_ARGUMENTo 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

Acho que você gosta

Origin blog.csdn.net/jiaobuchong/article/details/104571958
Recomendado
Clasificación