Dubbo捕获自定义异常问题

起因

最近在工作时,发现的一个问题——Dubbo无法捕获自定义异常。

dubbo服务提供者无法捕捉自定义异常的问题解决 | 田小晖 (tianch.xyz)

https://blog.csdn.net/qq_36827957/article/details/89509749

可直接看参考原文,如上

正文

我遇到的问题是这样的:

基于Dubbo的项目结构分为apiprivideropenapiapi定义DubboRPC接口,provideropenapi都引用apiproviderapi接口的实现方也是服务实际提供者,openapi是服务消费者。在openapiController中调用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异常抛出(重要)

解决方案

1、在方法上声明自定义异常 也就是throws 自定义异常

2、将异常和接口放到同一个包下

3、重写一个ExceptionFilter替代dubbo的ExceptionFilter

针对以上三种方案,对于自己的项目自然有合适的解法

方案一

最为简单,只需要在声明接口方法时抛出一场即可

public interface HelloService {

    String sayHello(String name) throws ServiceException;
}

方案二

对于小规模的微服务这个也是比较容易做到的,只要有公共的common模块,在使用时做到统一即可

方案三

没有深究过,但要替代Dubbo?非常不建议

相关文章

学习编程是顺着互联网的发展潮流,是一件好事。新手如何学习...
IT行业是什么工作做什么?IT行业的工作有:产品策划类、页面...
女生学Java好就业吗?女生适合学Java编程吗?目前有不少女生...
Can’t connect to local MySQL server through socket \'/v...
oracle基本命令 一、登录操作 1.管理员登录 # 管理员登录 ...
一、背景 因为项目中需要通北京网络,所以需要连vpn,但是服...