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:
- If it is
checked
Exception, directly thrown - There is a declaration on the method signature that is thrown directly
- 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 same
jar
In the bag, straight out - is
JDK
A built-in exception is thrown directly - is
Dubbo
Its own exception is thrown directly - Otherwise, wrap into
RuntimeException
Throw to the client
- Exception classes and interface classes are the same
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:
- Custom one
ExceptionFilter
莊Dubbo
Use, compatible with their own business exception classes - Write an AOP on the provider side to intercept all exceptions and handle them yourself
- the
unchecked
Abnormal insteadchecked
abnormal
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 đ