Hi everyone, I am Java class representative. Today we talk about Dubbo.

Dubbo, as a high-performance RPC framework, is widely used in microservices architecture. Based on an exception processing in the development process, this paper deeply analyzes the exception processing logic of Dubbo, and gives the best practice of Dubbo exception processing combined with the source code.

1 background

In the daily business development process, we usually customize some business exceptions in order to make the business code more robust and return more friendly prompts when encountering errors. Based on service requirements, exceptions can be customized and non-checked

Knowledge review

The Exception class and its subclasses, excluding those of RuntimeException, are collectively called checked exceptions. If such exceptions can be thrown during method execution, they must be declared on the method signature

The RuntimeException class and its subclasses are collectively called unchecked exceptions. If such exceptions can be thrown during method execution, you do not need to declare them on the method signature

The project in charge of the class representative used SpringCloudAlibaba to implement microservices. During the development, the brothers in the group encountered a problem: When a Dubbo RPC call is made, the provider throws a business class exception that is not checked, but the consumer receives a RuntimeException and the message is concatenated with the stack information.

2 Problem recurrence

In Dubbo microservices, providers are divided into APIS and Services. Consumers only need to introduce apis to invoke service instances from the registry.

When a service throws a custom unchecked exception that does not exist in the corresponding API package, the exception is wrapped as a RuntimeException.

Dubbo is an RPC framework where clients call remote methods, and the parameters and return values are serialized and deserialized as byte arrays. The consumer must recognize this exception for deserialization to succeed.

Obviously, Dubbo didn’t think the consumer knew the exception and wrapped it to avoid deserialization failure.

Dubbo’s exception handling mechanism is described below.

3 Source code Analysis

Exceptions to the Dubbo remote call are handled by the ExceptionFilter class

public Result invoke(Invoker
        invoker, Invocation invocation) throws RpcException {
        try {
            Result result = invoker.invoke(invocation);
            if(result.hasException() && GenericService.class ! = invoker.getInterface()) {try {
                    Throwable exception = result.getException();

                    // If it is a checked exception, throw it
                    if (! (exception instanceof RuntimeException) && (exception instanceof Exception)) {
                        return result;
                    }
                    // There is a declaration on the method signature, which is thrown directly
                    try{ Method method = invoker.getInterface().getMethod(invocation.getMethodName(), invocation.getParameterTypes()); Class<? >[] exceptionClassses = method.getExceptionTypes();for(Class<? > exceptionClass : exceptionClassses) {if (exception.getClass().equals(exceptionClass)) {
                                returnresult; }}}catch (NoSuchMethodException e) {
                        return result;
                    }

                    // ERROR logs are printed on the server side for exceptions not defined on the method signature
                    logger.error("Got unchecked and undeclared exception which called by " + RpcContext.getContext().getRemoteHost()
                            + ". service: " + invoker.getInterface().getName() + ", method: " + invocation.getMethodName()
                            + ", exception: " + exception.getClass().getName() + ":" + exception.getMessage(), exception);

                    // The exception class and the interface class are in the same JAR package, directly thrown
                    String serviceFile = ReflectUtils.getCodeBase(invoker.getInterface());
                    String exceptionFile = ReflectUtils.getCodeBase(exception.getClass());
                    if (serviceFile == null || exceptionFile == null || serviceFile.equals(exceptionFile)){
                        return result;
                    }
                    // is a JDK exception thrown directly
                    String className = exception.getClass().getName();
                    if (className.startsWith("java.") || className.startsWith("javax.")) {
                        return result;
                    }
                    // Is an exception of Dubbo itself, thrown directly
                    if (exception instanceof RpcException) {
                        return result;
                    }

                    // Otherwise, wrap it as RuntimeException and throw it to the client
                    return new RpcResult(new RuntimeException(StringUtils.toString(exception)));
                } catch (Throwable e) {
                    logger.warn("Fail to ExceptionFilter when called by " + RpcContext.getContext().getRemoteHost()
                            + ". service: " + invoker.getInterface().getName() + ", method: " + invocation.getMethodName()
                            + ", exception: " + e.getClass().getName() + ":" + e.getMessage(), e);
                    returnresult; }}return result;
        } catch (RuntimeException e) {
            logger.error("Got unchecked and undeclared exception which called by " + RpcContext.getContext().getRemoteHost()
                    + ". service: " + invoker.getInterface().getName() + ", method: " + invocation.getMethodName()
                    + ", exception: " + e.getClass().getName() + ":" + e.getMessage(), e);
            throw e;
        }
Copy the code

As you can see from the source code, the main function of this class is to return the exception thrown by the interface, which Dubbo defines as follows:

  1. If it ischeckedException, directly thrown
  2. There is a declaration on the method signature that is thrown directly
  3. An error that does not match 1,2 is considered an error, and an error log is printed.
    • Exception classes and interface classes are the samejarIn the bag, straight out
    • isJDKA built-in exception is thrown directly
    • isDubboIts own exception is thrown directly
    • Otherwise, wrap intoRuntimeExceptionThrow to the client

In fact, Dubbo, as an RPC framework, takes all possible exceptions into account and eventually wraps a RuntimeException in case the deserialization fails if Dubbo thinks the consumer doesn’t recognize the exception.

If the consumer can’t find the exception thrown by the provider, it must be the developer’s fault, and Dubbo would be wrong to blame it.

4 Best Practices

Dubbo’s website ->Dubbo 2.7-> User Documentation -> Servicification Best Practices are described as follows:

The subcontract

It is recommended to put service interfaces, service models, service exceptions, and so on in the API package, because service models and exceptions are part of the API, and this is also consistent with the principles of subcontracting: Reuse publish Equivalence (REP), common Reuse (CRP).

Therefore, provider-APIS that conform to Dubbo best practices should contain service interface packages, service model packages, and service exception packages. All exceptions used in a service should be declared in the API package so that the consumer calls the service in accordance with Dubbo requirements:

Exception classes and interface classes are in the same JAR and thrown directly

This prevents Dubbo from wrapping RuntimeException and throwing it to the client.

Therefore, for the problems encountered at the start of the article, we just need to throw the provider-service to define the user-defined non-checked exception in the provider- API, and throw the corresponding method. This can not only prevent Dubbo packaging. Dubbo does not report an error because no exception is declared in the method signature. Also, because the exception is not checked, the client is not forced to try catch the method.

A reference to the subcontracting practice:

+ - SCR | + - demo | + - domain (business domain to transmit data with DTO) | + - the service implementation class service interface (API) | + - the exception of custom exception (business domain)Copy the code

5 detours

If you Google the keyword [Dubbo exception handling], you’ll find almost every article along the following lines:

  1. Custom oneExceptionFilter 莊DubboUse, compatible with their own business exception classes
  2. Write an AOP on the provider side to intercept all exceptions and handle them yourself
  3. theuncheckedAbnormal insteadcheckedabnormal

Of course, all of the above can solve the problem, but isn’t that just killing chickens?

When it is clear that the code development is not standard and does not follow best practices, it is forced to blame the underlying framework. While Dubbo is trying to be generic, this approach makes the code tightly coupled.

To summarize the essence of the problem: Dubbo wraps the exception to prevent deserialization failures when he thinks the consumer cannot find the exception class. For this essence, we can solve it with the simplest, most efficient and least impact method.

Class representatives believe that readers should have their own judgment combined with Dubbo exception handling source code.

6 reflection

In most cases, we will find the answer to the problems we encounter. For the same problem, there may be a variety of solutions. What we need to do is to find the essence of the problem, draw inferences from one another, and choose the most appropriate solution according to the actual situation of our business.

Do not follow blindly, notice: all letter is better than no book.


【 Previous recommendation 】

MySQL priority queue (order by limit)

Download attachment name total garbled? It’s time you read the RFC documentation!

Freemarker Tutorial (I)- Template development Manual

RabbitMQ official tutorial translation


👇 code word is not easy, follow the Java class representative, get the latest Java dry goods 👇