文章目录

  • 引言
  • 问题重现
  • 问题解决
  • 禁用provider的ExceptionFilter
  • GlobalExceptionHandler捕获ServiceException
  • 效果展示
  • 参考


引言

在使用sprinboot+dubbo+zookeeper实现的微服务项目中,自定义异常在由服务提供者传给服务消费者时异常信息会丢失。

问题重现

项目进行统一异常处理,抽取了一个通用异常 ServiceException,此异常是非受检异常,即继承于 RuntimeException。发现如果服务提供方即 provider 抛出了 ServiceException 异常,consumer 服务消费方就会收到一个 RuntimeException 异常,而 ServiceException 异常的内容被包含在了 RuntimeException 的异常堆栈中

[Request processing failed; nested exception is java.lang.RuntimeException: io.github.mosiki.common.exception.ServiceException: missing_required_parameters
io.github.mosiki.common.exception.ServiceException: missing_required_parameters
    at io.github.mosiki.provider.HelloService.sayHello(HelloService.java:20)
    at com.alibaba.dubbo.common.bytecode.Wrapper1.invokeMethod(Wrapper1.java)

而我的统一异常处理是这样的,只处理 ServiceException 以及 Exception,因此就无法获取到原始异常的信息了。

@Slf4j
@RestControllerAdvice
public class GlobalExceptionHandler {
 
    @ExceptionHandler(ServiceException.class)
    public Result handlerServiceException(ServiceException ex) {
        return Result.failure(ex.getCode(), ex.getMessage());
    }
 
    @ExceptionHandler({Exception.class})
    public Result handlerException(Exception ex) {
        log.error("发生未知异常:{}", ex);
        return Result.failure(HttpStatus.INTERNAL_SERVER_ERROR.value(), "服务器打了个小盹儿~请稍候再试");
    }
}

访问接口将返回如下,异常中原有信息丢失。

上网搜索发现,这是因为 dubbo 的异常处理类 com.alibaba.dubbo.rpc.filter.ExceptionFilter 进行处理后的结果,Debug 之后确实如此,dubbo 在此进行了转换。

问题解决

这里我采用的第一种方式,禁用 provider 的 ExceptionFilter

现在我想要 provider 把自定义的异常原封不动的抛给 consumer 进行处理,于是有了如下思路:

  1. 禁用 provider 的 ExceptionFilter
  2. 让 GlobalExceptionHandler 处理 consumer 的异常

按照此思路做就很简单了,网上大多文章的办法都比较麻烦,有用 AOP 处理的,甚至还有让自己修改编译源码上传私服的-_-||,本文给出比较简便的方法,提供参考。

禁用provider的ExceptionFilter

修改 provider 的配置,我这里使用 yml 配置文件,其他类型如 xml/properties 也同理,设置 provider 的 filter 为 -exception,这样异常就不会被处理而是直接抛出了。

dubbo:
  application:
    name: provider
  protocol:
    name: dubbo
    port: 20100
  registry:
    address: 127.0.0.1:2181
    protocol: zookeeper
  provider:
    filter: -exception

GlobalExceptionHandler捕获ServiceException

只是禁用了 provider 的 ExceptionHandler 还不能完全达到我们的目的,访问接口,provider 抛出异常 consumer 正确接收为 ServiceException

[Request processing failed; nested exception is io.github.mosiki.common.exception.ServiceException: missing_required_parameters] with root cause
 
io.github.mosiki.common.exception.ServiceException: missing_required_parameters
    at io.github.mosiki.provider.HelloService.sayHello(HelloService.java:20) ~[na:na]
    at com.alibaba.dubbo.common.bytecode.Wrapper1.invokeMethod(Wrapper1.java) ~[na:na]

我们处理一下 GlobalExceptionHandler。

SpringBoot 主要这个启动类的位置和全局异常处理器的位置,一定要保证异常处理器在启动类的同级包或者在启动类的子包当中,否则异常处理器将不生效!

效果展示

以上两步完成后,重启服务,访问接口测试。

拿到了 provider 抛出的原始自定义异常,如此问题就解决了。

dubbo MergeableCluster 使用场景 dubbo:parameter_java