訪問個人博客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 拋出的原始自定義異常,如此問題就解決了。