访问个人博客java
目前计划对已有的单体项目进行组织架构拆分,调研了分布式系统中经常使用中间件 Dubbo 和 Spring Cloud,选择了 Dubbo,能够对当前现有项目进行平滑升级改造。可是一开始就遇到了麻烦,自定义异常在传递的过程当中变成了 RuntimeException,统一异常处理 GlobalExceptionHandler 没法获取异常信息。git
<!--more-->github
问题重现
项目进行统一异常处理,抽取了一个通用异常 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(), "服务器打了个小盹儿~请稍候再试"); } }
访问接口将返回以下,异常中原有信息丢失。
app
上网搜索发现,这是由于 dubbo 的异常处理类 com.alibaba.dubbo.rpc.filter.ExceptionFilter
进行处理后的结果,Debug 以后确实如此,dubbo 在此进行了转换。
分布式
问题解决之道
如今我想要 provider 把自定义的异常原封不动的抛给 consumer 进行处理,因而有了以下思路:ide
- 禁用 provider 的 ExceptionFilter
- 让 GlobalExceptionHandler 处理 consumer 的异常
按照此思路作就很简单了,网上大多文章的办法都比较麻烦,有用 AOP 处理的,甚至还有让本身修改编译源码上传私服的-_-||,本文给出比较简便的方法,提供参考。测试
禁用provider的ExceptionFilter
修改 provider 的配置,我这里使用 yml 配置文件,其余类型如 xml/properties 也同理,设置 provider 的 filter 为 -exception,这样异常就不会被处理而是直接抛出了。ui
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 抛出的原始自定义异常,如此问题就解决了。