• 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
     * 

    * Functions: *

      *
    1. unexpected exception will be logged in ERROR level on provider side. Unexpected exception are unchecked * exception not declared on the interface
    2. *
    3. 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
    4. *
    */
    @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
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88

    流程描述如下

    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;
    }
    
    • 1
    • 2
    • 3
    • 4

    方案二

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

    方案三

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

  • 相关阅读:
    Win11校园网无法连接怎么办?Win11连接不到校园网的解决方法
    深度强化学习01
    从迷之自信到逻辑自信(简版)
    向某文件中逐秒追加带序号输入当前时间 fgets fputs fprintf sprintf
    【校招VIP】前端操作系统之存储管理
    LeetCode220902_93、搜索二维矩阵 II
    集群规模:3 FE + 89 BE
    软件系统运维方案
    【STM32】MDK下的C语言基础
    CrossOver 23.6 让Mac可以运行Windows程序的工具
  • 原文地址:https://blog.csdn.net/weixin_44783934/article/details/126506288