自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

Spring Cloud實(shí)戰(zhàn)小貼士:Zuul統(tǒng)一異常處理(一)

開發(fā) 開發(fā)工具
在之前的文章中,我們詳細(xì)介紹了Spring Cloud Zuul中自己實(shí)現(xiàn)的一些核心過濾器,以及這些過濾器在請求生命周期中的不同作用。本文將介紹如何利用error過濾器來實(shí)現(xiàn)統(tǒng)一的異常處理。

在上一篇《Spring Cloud源碼分析(四)Zuul:核心過濾器》一文中,我們詳細(xì)介紹了Spring Cloud Zuul中自己實(shí)現(xiàn)的一些核心過濾器,以及這些過濾器在請求生命周期中的不同作用。我們會發(fā)現(xiàn)在這些核心過濾器中并沒有實(shí)現(xiàn)error階段的過濾器。那么這些過濾器可以用來做什么呢?接下來,本文將介紹如何利用error過濾器來實(shí)現(xiàn)統(tǒng)一的異常處理。

[[191637]]

過濾器中拋出異常的問題

首先,我們可以來看看默認(rèn)情況下,過濾器中拋出異常Spring Cloud Zuul會發(fā)生什么現(xiàn)象。我們創(chuàng)建一個(gè)pre類型的過濾器,并在該過濾器的run方法實(shí)現(xiàn)中拋出一個(gè)異常。比如下面的實(shí)現(xiàn),在run方法中調(diào)用的doSomething方法將拋出RuntimeException異常。

  1. public class ThrowExceptionFilter extends ZuulFilter { 
  2.     private static Logger log = LoggerFactory.getLogger(ThrowExceptionFilter.class); 
  3.     @Override 
  4.     public String filterType() { 
  5.         return "pre"; 
  6.     } 
  7.     @Override 
  8.     public int filterOrder() { 
  9.         return 0; 
  10.     } 
  11.     @Override 
  12.     public boolean shouldFilter() { 
  13.         return true; 
  14.     } 
  15.     @Override 
  16.     public Object run() { 
  17.         log.info("This is a pre filter, it will throw a RuntimeException"); 
  18.         doSomething(); 
  19.         return null; 
  20.     } 
  21.     private void doSomething() { 
  22.         throw new RuntimeException("Exist some errors..."); 
  23.     } 

運(yùn)行網(wǎng)關(guān)程序并訪問某個(gè)路由請求,此時(shí)我們會發(fā)現(xiàn):在API網(wǎng)關(guān)服務(wù)的控制臺中輸出了ThrowExceptionFilter的過濾邏輯中的日志信息,但是并沒有輸出任何異常信息,同時(shí)發(fā)起的請求也沒有獲得任何響應(yīng)結(jié)果。為什么會出現(xiàn)這樣的情況呢?我們又該如何在過濾器中處理異常呢?

解決方案一:嚴(yán)格的try-catch處理

回想一下,我們在上一節(jié)中介紹的所有核心過濾器,是否還記得有一個(gè)post過濾器SendErrorFilter是用來處理異常信息的?根據(jù)正常的處理流程,該過濾器會處理異常信息,那么這里沒有出現(xiàn)任何異常信息說明很有可能就是這個(gè)過濾器沒有被執(zhí)行。所以,我們不妨來詳細(xì)看看SendErrorFilter的shouldFilter函數(shù):

  1. public boolean shouldFilter() { 
  2.     RequestContext ctx = RequestContext.getCurrentContext(); 
  3.     return ctx.containsKey("error.status_code") && !ctx.getBoolean(SEND_ERROR_FILTER_RAN, false); 

可以看到該方法的返回值中有一個(gè)重要的判斷依據(jù)ctx.containsKey("error.status_code"),也就是說請求上下文中必須有error.status_code參數(shù),我們實(shí)現(xiàn)的ThrowExceptionFilter中并沒有設(shè)置這個(gè)參數(shù),所以自然不會進(jìn)入SendErrorFilter過濾器的處理邏輯。那么我們要如何用這個(gè)參數(shù)呢?我們可以看一下route類型的幾個(gè)過濾器,由于這些過濾器會對外發(fā)起請求,所以肯定會有異常需要處理,比如RibbonRoutingFilter的run方法實(shí)現(xiàn)如下:

  1. public Object run() { 
  2.     RequestContext context = RequestContext.getCurrentContext(); 
  3.     this.helper.addIgnoredHeaders(); 
  4.     try { 
  5.         RibbonCommandContext commandContext = buildCommandContext(context); 
  6.         ClientHttpResponse response = forward(commandContext); 
  7.         setResponse(response); 
  8.         return response; 
  9.     } 
  10.     catch (ZuulException ex) { 
  11.         context.set(ERROR_STATUS_CODE, ex.nStatusCode); 
  12.         context.set("error.message", ex.errorCause); 
  13.         context.set("error.exception", ex); 
  14.     } 
  15.     catch (Exception ex) { 
  16.         context.set("error.status_code", HttpServletResponse.SC_INTERNAL_SERVER_ERROR); 
  17.         context.set("error.exception", ex); 
  18.     } 
  19.     return null; 

可以看到,整個(gè)發(fā)起請求的邏輯都采用了try-catch塊處理。在catch異常的處理邏輯中并沒有做任何輸出操作,而是往請求上下文中添加一些error相關(guān)的參數(shù),主要有下面三個(gè)參數(shù):

  • error.status_code:錯(cuò)誤編碼
  • error.exception:Exception異常對象
  • error.message:錯(cuò)誤信息

其中,error.status_code參數(shù)就是SendErrorFilter過濾器用來判斷是否需要執(zhí)行的重要參數(shù)。分析到這里,實(shí)現(xiàn)異常處理的大致思路就開始明朗了,我們可以參考RibbonRoutingFilter的實(shí)現(xiàn)對ThrowExceptionFilter的run方法做一些異常處理的改造,具體如下:

  1. public Object run() { 
  2.     log.info("This is a pre filter, it will throw a RuntimeException"); 
  3.     RequestContext ctx = RequestContext.getCurrentContext(); 
  4.     try { 
  5.         doSomething(); 
  6.     } catch (Exception e) { 
  7.         ctx.set("error.status_code", HttpServletResponse.SC_INTERNAL_SERVER_ERROR); 
  8.         ctx.set("error.exception", e); 
  9.     } 
  10.      return null; 

通過上面的改造之后,我們再嘗試訪問之前的接口,這個(gè)時(shí)候我們可以得到如下響應(yīng)內(nèi)容:

  1.     "timestamp": 1481674980376, 
  2.     "status": 500, 
  3.     "error": "Internal Server Error", 
  4.     "exception": "java.lang.RuntimeException", 
  5.     "message": "Exist some errors..." 

此時(shí),我們的異常信息已經(jīng)被SendErrorFilter過濾器正常處理并返回給客戶端了,同時(shí)在網(wǎng)關(guān)的控制臺中也輸出了異常信息。從返回的響應(yīng)信息中,我們可以看到幾個(gè)我們之前設(shè)置在請求上下文中的內(nèi)容,它們的對應(yīng)關(guān)系如下:

  • status:對應(yīng)error.status_code參數(shù)的值
  • exception:對應(yīng)error.exception參數(shù)中Exception的類型
  • message:對應(yīng)error.exception參數(shù)中Exception的message信息。對于message的信息,我們在過濾器中還可以通過ctx.set("error.message", "自定義異常消息");來定義更友好的錯(cuò)誤信息。SendErrorFilter會優(yōu)先取error.message來作為返回的message內(nèi)容,如果沒有的話才會使用Exception中的message信息

解決方案二:ErrorFilter處理

通過上面的分析與實(shí)驗(yàn),我們已經(jīng)知道如何在過濾器中正確的處理異常,讓錯(cuò)誤信息能夠順利地流轉(zhuǎn)到后續(xù)的SendErrorFilter過濾器來組織和輸出。但是,即使我們不斷強(qiáng)調(diào)要在過濾器中使用try-catch來處理業(yè)務(wù)邏輯并往請求上下文添加異常信息,但是不可控的人為因素、意料之外的程序因素等,依然會使得一些異常從過濾器中拋出,對于意外拋出的異常又會導(dǎo)致沒有控制臺輸出也沒有任何響應(yīng)信息的情況出現(xiàn),那么是否有什么好的方法來為這些異常做一個(gè)統(tǒng)一的處理呢?

這個(gè)時(shí)候,我們就可以用到error類型的過濾器了。由于在請求生命周期的pre、route、post三個(gè)階段中有異常拋出的時(shí)候都會進(jìn)入error階段的處理,所以我們可以通過創(chuàng)建一個(gè)error類型的過濾器來捕獲這些異常信息,并根據(jù)這些異常信息在請求上下文中注入需要返回給客戶端的錯(cuò)誤描述,這里我們可以直接沿用在try-catch處理異常信息時(shí)用的那些error參數(shù),這樣就可以讓這些信息被SendErrorFilter捕獲并組織成消息響應(yīng)返回給客戶端。比如,下面的代碼就實(shí)現(xiàn)了這里所描述的一個(gè)過濾器:

  1. public class ErrorFilter extends ZuulFilter { 
  2.     Logger log = LoggerFactory.getLogger(ErrorFilter.class); 
  3.     @Override 
  4.     public String filterType() { 
  5.         return "error"; 
  6.     } 
  7.     @Override 
  8.     public int filterOrder() { 
  9.         return 10; 
  10.     } 
  11.     @Override 
  12.     public boolean shouldFilter() { 
  13.         return true; 
  14.     } 
  15.     @Override 
  16.     public Object run() { 
  17.         RequestContext ctx = RequestContext.getCurrentContext(); 
  18.         Throwable throwable = ctx.getThrowable(); 
  19.         log.error("this is a ErrorFilter : {}", throwable.getCause().getMessage()); 
  20.         ctx.set("error.status_code", HttpServletResponse.SC_INTERNAL_SERVER_ERROR); 
  21.         ctx.set("error.exception", throwable.getCause()); 
  22.         return null; 
  23.     } 

在將該過濾器加入到我們的API網(wǎng)關(guān)服務(wù)之后,我們可以嘗試使用之前介紹try-catch處理時(shí)實(shí)現(xiàn)的ThrowExceptionFilter(不包含異常處理機(jī)制的代碼),讓該過濾器能夠拋出異常。這個(gè)時(shí)候我們再通過API網(wǎng)關(guān)來訪問服務(wù)接口。此時(shí),我們就可以在控制臺中看到ThrowExceptionFilter過濾器拋出的異常信息,并且請求響應(yīng)中也能獲得如下的錯(cuò)誤信息內(nèi)容,而不是什么信息都沒有的情況了。

  1.     "timestamp": 1481674993561, 
  2.     "status": 500, 
  3.     "error": "Internal Server Error", 
  4.     "exception": "java.lang.RuntimeException", 
  5.     "message": "Exist some errors..." 

【本文為51CTO專欄作者“翟永超”的原創(chuàng)稿件,轉(zhuǎn)載請通過51CTO聯(lián)系作者獲取授權(quán)】

戳這里,看該作者更多好文

責(zé)任編輯:趙寧寧 來源: 51CTO專欄
相關(guān)推薦

2017-05-19 15:13:05

過濾器Spring ClouZuul

2017-07-31 15:47:50

Zuul統(tǒng)一處理

2017-05-02 23:05:44

HTTPZuulCookie

2017-10-20 14:55:06

Spring ClouZuul加載

2017-10-18 16:00:14

SpringCloudZuul路徑

2025-02-13 00:34:22

Spring對象系統(tǒng)

2021-04-30 07:34:01

Spring BootController項(xiàng)目

2023-11-28 14:32:04

2021-06-29 19:26:29

緩存Spring CachSpring

2025-04-09 08:00:00

FastAPI統(tǒng)一響應(yīng)全局異常處理

2017-08-10 16:14:07

FeignRPC模式

2024-08-09 08:25:32

Spring流程注解

2017-09-26 16:17:39

Ribboneager-load模式

2019-08-22 14:02:00

Spring BootRestful APIJava

2024-10-28 08:32:22

統(tǒng)一接口響應(yīng)SpringBoot響應(yīng)框架

2022-05-07 10:09:01

開發(fā)Java日志

2022-05-30 08:03:06

后端參數(shù)校驗(yàn)異常處理

2023-11-30 07:00:56

SpringBoot處理器

2017-04-12 14:43:01

Spring ClouZuul過濾器

2024-08-05 10:03:53

點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號