直入主题,我们都知道dubbo是一个rpc的框架,consumer是通过网络对provider发起方法调用的,provider也要通过网络返回响应。那如果provider端发生了异常而又没有捕获,照理就不会有返回值,consumer端会得到什么样的结果呢?
其实dubbo有一个专门处理异常的Filter,叫ExceptionFilter,针对provider端未被捕获的各种异常做了分别处理,看一下源码:
/**
* ExceptionInvokerFilter
* <p>
* Functions:
* <ol>
* <li>unexpected exception will be logged in ERROR level on provider side. Unexpected exception are unchecked
* exception not declared on the interface</li>
* <li>Wrap the exception not introduced in API package into RuntimeException. Framework will serialize the outer exception but stringnize its cause in order to avoid of possible serialization problem on client side</li>
* </ol>
*/
@Activate(group = Constants.PROVIDER)
public class ExceptionFilter implements Filter {
private final Logger logger;
public ExceptionFilter() {
this(LoggerFactory.getLogger(ExceptionFilter.class));
}
public ExceptionFilter(Logger logger) {
this.logger = logger;
}
@Override
public Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException {
try {
return invoker.invoke(invocation);
} 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;
}
}
@Override
public Result onResponse(Result result, Invoker<?> invoker, Invocation invocation) {
if (result.hasException() && GenericService.class != invoker.getInterface()) {
try {
Throwable exception = result.getException();
// directly throw if it's checked exception
if (!(exception instanceof RuntimeException) && (exception instanceof Exception)) {
return result;
}
// directly throw if the exception appears in the signature
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;
}
// for the exception not found in method's signature, print ERROR message in server's log.
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);
// directly throw if exception class and interface class are in the same jar file.
String serviceFile = ReflectUtils.getCodeBase(invoker.getInterface());
String exceptionFile = ReflectUtils.getCodeBase(exception.getClass());
if (serviceFile == null || exceptionFile == null || serviceFile.equals(exceptionFile)) {
return result;
}
// directly throw if it's JDK exception
String className = exception.getClass().getName();
if (className.startsWith("java.") || className.startsWith("javax.")) {
return result;
}
// directly throw if it's dubbo exception
if (exception instanceof RpcException) {
return result;
}
// otherwise, wrap with RuntimeException and throw back 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);
return result;
}
}
return result;
}
}
这里的Result封装了rpc调用的返回值,其实代码里各个逻辑已经用英文注释的很清楚了,总结一下(以下说的抛出是指异常会传递给consumer):
- 如果是必检异常,则直接抛出;
- 如果方法签名声明了该异常,则直接抛出;
- 如果异常类和服务api声明在同一个jar包,则直接抛出;
- 如果是JDK自带异常(包名以java或者javax开头),则直接抛出;
- 如果是dubbo框架异常(RpcException),则直接抛出;
- 其他异常,包装成RuntimeException后抛出(为了避免序列化问题会先将异常堆栈转成字符串)。
模拟一下各种情况,看看dubbo源码是否说到做到了O(∩_∩)O哈哈~
provider启动类
public class Application {
public static void main(String[] args) throws Exception {
ServiceConfig<DemoServiceImpl> service = new ServiceConfig<>();
service.setApplication(new ApplicationConfig("dubbo-demo-api-provider"));
service.setRegistry(new RegistryConfig("multicast://224.5.6.7:1234"));
service.setInterface(DemoService.class);
service.setRef(new DemoServiceImpl());
service.export();
System.in.read();
}
}
消费者
public class Application {
public static void main(String[] args) {
ReferenceConfig<DemoService> reference = new ReferenceConfig<>();
reference.setApplication(new ApplicationConfig("dubbo-demo-api-consumer"));
reference.setRegistry(new RegistryConfig("multicast://224.5.6.7:1234"));
reference.setInterface(DemoService.class);
DemoService service = reference.get();
String message = null;
try {
message = service.sayHello("dubbo");
} catch (Exception e) {
e.printStackTrace();
}
System.out.println(message);
}
}
必检异常 && 方法签名声明
因为必检异常要么捕获,要么你声明,而捕获后就不会往外抛了,所以这里两种场景一起测试
服务接口
public interface DemoService {
String sayHello(String name) throws IOException;
}
服务实现类
public class DemoServiceImpl implements DemoService {
private static final Logger logger = LoggerFactory.getLogger(DemoServiceImpl.class);
@Override
public String sayHello(String name) throws IOException {
logger.info("Hello " + name + ", request from consumer: " + RpcContext.getContext().getRemoteAddress());
throw new IOException();
}
}
运行后看到,验证没问题
同一个jar包
服务接口
public interface DemoService {
String sayHello(String name);
}
服务实现类
public class DemoServiceImpl implements DemoService {
private static final Logger logger = LoggerFactory.getLogger(DemoServiceImpl.class);
@Override
public String sayHello(String name) {
logger.info("Hello " + name + ", request from consumer: " + RpcContext.getContext().getRemoteAddress());
throw new TestException();
}
}
自定义一个RuntimeException,放于api相同的jar包中
运行后看到,验证没问题
JDK自带异常
服务接口
public interface DemoService {
String sayHello(String name);
}
服务实现类
public class DemoServiceImpl implements DemoService {
private static final Logger logger = LoggerFactory.getLogger(DemoServiceImpl.class);
@Override
public String sayHello(String name) {
logger.info("Hello " + name + ", request from consumer: " + RpcContext.getContext().getRemoteAddress());
throw new NullPointerException();
}
}
运行后看到,验证没问题
dubbo框架异常
服务接口
public interface DemoService {
String sayHello(String name);
}
服务实现类
public class DemoServiceImpl implements DemoService {
private static final Logger logger = LoggerFactory.getLogger(DemoServiceImpl.class);
@Override
public String sayHello(String name) {
logger.info("Hello " + name + ", request from consumer: " + RpcContext.getContext().getRemoteAddress());
}
}
打断点构造超时,运行后看到,验证没问题
其他异常
服务接口
public interface DemoService {
String sayHello(String name);
}
服务实现类
public class DemoServiceImpl implements DemoService {
private static final Logger logger = LoggerFactory.getLogger(DemoServiceImpl.class);
@Override
public String sayHello(String name) {
logger.info("Hello " + name + ", request from consumer: " + RpcContext.getContext().getRemoteAddress());
}
}
构造一个RuntimeException,放在provider实现的包中
运行后看到,验证没问题
网友评论