dubbo項目全局異常處理

一. 問題

dubbo項目中, 定義了全局異常處理類以後, 在provider中拋出的自定義異常在consumer中捕獲不到

二. 原因

dubbo源碼中對異常進行了異常過濾處理, 源碼入下:

package com.alibaba.dubbo.rpc.filter;
import ...
@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;
    }

    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;
        }
    }

}

三. 分析

dubbo服務提供者拋出的異常會被 com.alibaba.dubbo.rpc.filter.ExceptionFilter攔截處理,若是自定義的異常,會被包裝成RuntimeException拋給服務消費者。
ExceptionFilter處理邏輯:

(1)服務提供者實現了GenericService接口,直接拋出;

(2)如果是checked異常,直接拋出;

(3)在方法簽名上有聲明,直接拋出;

(4)異常類和接口類在同一jar包裏,直接拋出;

(5)是JDK自帶的異常,直接拋出;

(6)是Dubbo本身的異常,直接拋出;

(7)否則,包裝成RuntimeException拋給客戶端。

解決辦法

(1)服務提供者實現GenericService接口(有較多人使用);

(2)使用checked異常;

(3)服務提供者的api方法上直接throws自定義異常(也有較多人使用);

(4)自定義異常類放在服務提供者的jar包裏;

(5)實現dubbo的filter,自定義provider的異常處理邏輯;

(6)修改dubbo的ExceptionFilter源碼,將自定義異常加入拋出支持後重新打成jar包。

最後使用辦法

暴力辦法: 修改配置禁用provider的ExceptionFilter

#如果是spring項目
<dubbo:provider filter="-exception" />

#如果是springboot項目
provider:
  filter:-exception
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章