Hi, everyone. I am Java class representative . Today we will talk about Dubbo.
Recommendation: As a high-performance RPC framework, Dubbo is widely used in microservice architecture. Based on an exception handling in the development process, this article analyzes Dubbo's exception handling logic in depth, and combines the source code to give Dubbo exception handling Best practices.
1 background
In the daily business development process, we generally customize some business exceptions in order to make the business code more robust, and the prompts returned when errors are encountered are more friendly. According to business needs, it is divided into custom checked exceptions and unchecked exceptions
Knowledge point review
Exception
class and its subclasses, but notRuntimeException
, are collectively referred to as checked exceptions. If such an exception is likely to be thrown during the execution of the method, it must be declared on the method signature
RuntimeException
category and its subcategories are collectively referred to as unchecked exceptions. If this type of exception may be thrown during the execution of the method, it is not necessary to declare on the method signature
The project in charge of the class representative used SpringCloudAlibaba
land the microservices. During the development, the brothers in the group encountered a problem: When Dubbo RPC
called, a business class provider
consumer
, 060f4ce9a0daf3 received RuntimeException
and message
The stack information is spliced together.
2 Problem recurring
Dubbo
, provider
divided into api
and service
. consumer
only needs to introduce api
service
instance from the registry.
When service
is thrown in api
package, the exception will be packaged as RuntimeException
.
In fact, when the problem is analyzed, there is basically RPC
Dubbo
is a 060f4ce9a0db9f framework. The client calls all remote methods, and the parameters and return values are serialized and deserialized for byte array transmission. consumer
must recognize this exception in order to deserialize successfully.
Obviously, we throw the exception Dubbo
that consumer
does not recognize it. In order to avoid deserialization failure, the exception is wrapped.
The following describes Dubbo's exception handling mechanism in conjunction with the source code.
3 Source code analysis
Dubbo
remote call exceptions are handled ExceptionFilter
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();
// 如果是checked异常,直接抛出
if (! (exception instanceof RuntimeException) && (exception instanceof Exception)) {
return result;
}
// 在方法签名上有声明,直接抛出
try {
Method method = invoker.getInterface().getMethod(invocation.getMethodName(), invocation.getParameterTypes());
Class<?>[] exceptionClassses = method.getExceptionTypes();
for (Class<?> exceptionClass : exceptionClassses) {
if (exception.getClass().equals(exceptionClass)) {
return result;
}
}
} catch (NoSuchMethodException e) {
return result;
}
// 未在方法签名上定义的异常,在服务器端打印ERROR日志
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);
// 异常类和接口类在同一jar包里,直接抛出
String serviceFile = ReflectUtils.getCodeBase(invoker.getInterface());
String exceptionFile = ReflectUtils.getCodeBase(exception.getClass());
if (serviceFile == null || exceptionFile == null || serviceFile.equals(exceptionFile)){
return result;
}
// 是JDK自带的异常,直接抛出
String className = exception.getClass().getName();
if (className.startsWith("java.") || className.startsWith("javax.")) {
return result;
}
// 是Dubbo本身的异常,直接抛出
if (exception instanceof RpcException) {
return result;
}
// 否则,包装成RuntimeException抛给客户端
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);
return result;
}
}
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;
}
As you can see from the source code, the main function of this class is to return the exception thrown by the interface. Dubbo
defines it as the following situations:
- If it is
checked
exception, throw directly - There is a statement on the method signature, throw directly
Those that do not meet 1, 2 are considered errors, and the error log will be printed, and the following processing will be tried:
- The exception class and the interface class are in the same
jar
package, throw directly - It is an exception that comes with
JDK
- It is
Dubbo
itself, thrown directly - Otherwise, pack it into
RuntimeException
throw it to the client
- The exception class and the interface class are in the same
In fact, Dubbo
as the RPC
framework, has considered all kinds of exceptions. Finally, if Dubbo
thinks that consumer
does not recognize this exception, it will be packaged into RuntimeException
prevent deserialization from failing.
If this happens consumer
not find provider
by throwing an exception in this case, put it bluntly, it must be a problem for developers, put the blame Dubbo It would be very wrong of it!
4 best practices
Dubbo
official website ->Dubbo 2.7->User documentation->Serving best practice has the following description:
Subcontract
It is recommended to put service interfaces, service models, service exceptions, etc. in the API package, because the service model and exceptions are also part of the API. This is also in line with the principle of subcontracting: Reuse Release Equivalence Principle (REP), Common Reuse Principle (CRP) ).
Dubbo
, which conforms to the best practice of provider-api
, should include the service interface package, service model package, and service exception package. All service
used in abnormal, should be in api
statement package, such consumer
will meet the requirements of Dubbo when you call:
Exception class and interface class are in the same jar
package, throw directly
So as to avoid being packaged Dubbo
RuntimeException
to the client.
So, for the problems encountered at the beginning of the article, we just need to provider-service
thrown Unchecked custom exception provider-api
definition, while in the corresponding methods throw
out on it, both to prevent Dubbo
packaging, Dubbo
report a error
error because there is no exception declared in the method signature. try catch
exception, the client is not forced to perform 060f4ce9a0e17e on the method.
A reference subcontracting practice:
+- scr
|
+- demo
|
+- domain (业务域内传输数据用的 DTO)
|
+- service (API 中 service 接口的实现类)
|
+- exception (业务域中的自定义异常)
5 Detours
If Google keyword [Dubbo exception handling], you will find that almost all articles are based on the following ideas:
- Customize a
ExceptionFilter
forDubbo
use, compatible with your own business exception class - Write an AOP on the provider side to intercept all exceptions and handle them yourself
unchecked
exception tochecked
exception
Of course, the above methods can completely solve the problem, but does it mean to use a sledgehammer?
Obviously the code development is not standardized, and the best practice is not followed, but the underlying framework is forcibly blamed. Dubbo
is trying to be universal, but the above processing method is making the code tightly coupled.
Summarize the essence of the problem: Dubbo
thought that consumer
could not find the exception class, in order to prevent the deserialization failure, the exception was packaged. In view of this essence, we can solve it with the simplest, efficient, and least impactful method.
The class representative believes that readers should have their own judgment in Dubbo
6 reflection
Ask Google if something goes wrong. In most cases, we will find the answer to the problem we encounter. For the same problem, there may be many ways to solve it. What we need to do is to find the essence of the problem, draw inferences from one another, and based on the actual situation of our business. Choose the most suitable solution for the situation.
Don't follow blindly. Note: It is better to have no books than nothing.
[Recommended reading]
RabbitMQ official tutorial translation
Freemarker Tutorial (1)
uses Spring Validation to elegantly validate parameters
downloaded attachment name is always garbled? You should read the RFC document!
MySQL priority queue in a simple way (the order by limit problem you will definitely step on)
The codeword is not easy, welcome to like, follow and share.
Search: [ Java class representative ], follow the official account, update every day, and get more Java dry goods in time.
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。