起因
最近在工作时,发现的一个问题——dubbo
无法捕获自定义异常。
dubbo服务提供者无法捕捉自定义异常的问题解决 | 田小晖 (tianch.xyz)
https://blog.csdn.net/qq_36827957/article/details/89509749
可直接看参考原文,如上
正文
我遇到的问题是这样的:
基于dubbo
的项目结构分为api
、privider
、openapi
,api
定义dubbo
的RPC
接口,provider
和openapi
都引用api
,provider
是api
接口的实现方也是服务实际提供者,openapi
是服务消费者。在openapi
的Controller
中调用RPC
接口时,未能捕获到真正提供者provider
抛出的自定义异常。
遇到这个问题很是疑惑?
也尝试Debug
找到问题原由,Debug
过程中虽然能捕获到异常,但最后抛出的却是RuntimeException,并非我们的自定义异常
查看dubbo
源码,有这样的异常处理流程
/**
* 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 {
Result result = invoker.invoke(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;
} 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;
}
}
}
流程描述如下
1、如果是checked异常,直接抛出。
2、在方法签名上有声明,直接抛出。
3、异常类和接口类在同一jar包里,直接抛出。
4、是JDK自带的异常,直接抛出。
5、是dubbo本身的异常(RpcException),直接抛出。
6、否则,包装成RuntimeException抛给客户端。因为以上5点均不满足,所以该异常会被包装成RuntimeException异常抛出(重要)
解决方案
2、将异常和接口放到同一个包下
3、重写一个ExceptionFilter替代dubbo的ExceptionFilter
针对以上三种方案,对于自己的项目自然有合适的解法
方案一
最为简单,只需要在声明接口方法时抛出一场即可
public interface HelloService {
String sayHello(String name) throws ServiceException;
}
方案二
对于小规模的微服务这个也是比较容易做到的,只要有公共的common模块,在使用时做到统一即可
方案三
没有深究过,但要替代dubbo?非常不建议
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。