公司最近在做微服务改造的过程中,我们选取的是Apache Dubbo,并且定义了统一的返回值对象RpcResult。但是如果服务提供者端内部方法报错的话,对于消费者端来说就获取不到RpcResult了,如下图
没有实现dubbo全局异常处理器.png
现在想针对于Rpc调用也实现一个类似于使用spring-web的@ControllerAdvice与@ExceptionHandler来实现的全局异常处理器。经过查看Dubbo文档,可以使用Dubbo提供的Filter来实现,下将实现过程进行记录。

示例项目

结构

dubbo全局异常处理.png

服务提供者接口实现

/**
 * @author Bobby Cao
 * @date 2022/03/31 15:03
 */
@DubboService
public class DemoServiceImpl implements DemoService {

    @Override
    public RpcResult<String> sayHello(String name) {
        if (true) {
            throw new RuntimeException("test RpcExceptionFilter");
        }
        RpcResult<String> result = new RpcResult<>();
        result.setSuccess(Boolean.TRUE);
        result.setMessage("success");
        result.setData("hello " + name + "!");
        return result;
    }

}

创建RpcExceptionFilter全局异常处理器

实现参考与Dubbo内部自带的org.apache.dubbo.rpc.filter.ExceptionFilter

package cn.caofanqi.filter;

import cn.caofanqi.api.RpcResult;
import lombok.extern.slf4j.Slf4j;
import org.apache.dubbo.common.constants.CommonConstants;
import org.apache.dubbo.common.extension.Activate;
import org.apache.dubbo.rpc.Filter;
import org.apache.dubbo.rpc.Invocation;
import org.apache.dubbo.rpc.Invoker;
import org.apache.dubbo.rpc.Result;
import org.apache.dubbo.rpc.RpcException;
import org.apache.dubbo.rpc.service.GenericService;

/**
 * Rpc全局异常处理器
 *
 * 使用@Activate并指定group = CommonConstants.PROVIDER,使其只在服务提供者端生效
 *
 * @author Bobby Cao
 * @date 2022/03/31 11:17
 */
@Slf4j
@Activate(group = CommonConstants.PROVIDER)
public class RpcExceptionFilter implements Filter, Filter.Listener {

    @Override
    public Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException {
        return invoker.invoke(invocation);
    }

    @Override
    public void onResponse(Result appResponse, Invoker<?> invoker, Invocation invocation) {
        if (appResponse.hasException() && GenericService.class != invoker.getInterface()) {
            Throwable exception = appResponse.getException();
            RpcResult<Object> rpcResult = new RpcResult<>();
            rpcResult.setSuccess(Boolean.FALSE);
            rpcResult.setMessage(exception.getMessage());
            appResponse.setValue(rpcResult);
            appResponse.setException(null);
            log.error("RpcExceptionFilter catch exception msg:{}", exception.getMessage(), exception);
        }
    }

    @Override
    public void onError(Throwable t, Invoker<?> invoker, Invocation invocation) {
    }

}

META-INF/dubbo/org.apache.dubbo.rpc.Filter中添加自定义过滤器

dubbo的filter文件声明.png

测试

dubbo全局异常处理测试.png

项目示例源码:
https://github.com/caofanqi/dubbo-global-exception-handler

参考:
https://dubbo.apache.org/zh/docs/references/spis/filter