日本综合一区二区|亚洲中文天堂综合|日韩欧美自拍一区|男女精品天堂一区|欧美自拍第6页亚洲成人精品一区|亚洲黄色天堂一区二区成人|超碰91偷拍第一页|日韩av夜夜嗨中文字幕|久久蜜综合视频官网|精美人妻一区二区三区

RELATEED CONSULTING
相關(guān)咨詢
選擇下列產(chǎn)品馬上在線溝通
服務(wù)時(shí)間:8:30-17:00
你可能遇到了下面的問(wèn)題
關(guān)閉右側(cè)工具欄

新聞中心

這里有您想知道的互聯(lián)網(wǎng)營(yíng)銷解決方案
SpringCloud實(shí)戰(zhàn)小貼士:Zuul統(tǒng)一異常處理(一)

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

過(guò)濾器中拋出異常的問(wèn)題

首先,我們可以來(lái)看看默認(rèn)情況下,過(guò)濾器中拋出異常Spring Cloud Zuul會(huì)發(fā)生什么現(xiàn)象。我們創(chuàng)建一個(gè)pre類型的過(guò)濾器,并在該過(guò)濾器的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)程序并訪問(wèn)某個(gè)路由請(qǐng)求,此時(shí)我們會(huì)發(fā)現(xiàn):在API網(wǎng)關(guān)服務(wù)的控制臺(tái)中輸出了ThrowExceptionFilter的過(guò)濾邏輯中的日志信息,但是并沒(méi)有輸出任何異常信息,同時(shí)發(fā)起的請(qǐng)求也沒(méi)有獲得任何響應(yīng)結(jié)果。為什么會(huì)出現(xiàn)這樣的情況呢?我們又該如何在過(guò)濾器中處理異常呢?

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

回想一下,我們?cè)谏弦还?jié)中介紹的所有核心過(guò)濾器,是否還記得有一個(gè)post過(guò)濾器SendErrorFilter是用來(lái)處理異常信息的?根據(jù)正常的處理流程,該過(guò)濾器會(huì)處理異常信息,那么這里沒(méi)有出現(xiàn)任何異常信息說(shuō)明很有可能就是這個(gè)過(guò)濾器沒(méi)有被執(zhí)行。所以,我們不妨來(lái)詳細(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"),也就是說(shuō)請(qǐng)求上下文中必須有error.status_code參數(shù),我們實(shí)現(xiàn)的ThrowExceptionFilter中并沒(méi)有設(shè)置這個(gè)參數(shù),所以自然不會(huì)進(jìn)入SendErrorFilter過(guò)濾器的處理邏輯。那么我們要如何用這個(gè)參數(shù)呢?我們可以看一下route類型的幾個(gè)過(guò)濾器,由于這些過(guò)濾器會(huì)對(duì)外發(fā)起請(qǐng)求,所以肯定會(huì)有異常需要處理,比如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ā)起請(qǐng)求的邏輯都采用了try-catch塊處理。在catch異常的處理邏輯中并沒(méi)有做任何輸出操作,而是往請(qǐng)求上下文中添加一些error相關(guān)的參數(shù),主要有下面三個(gè)參數(shù):

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

其中,error.status_code參數(shù)就是SendErrorFilter過(guò)濾器用來(lái)判斷是否需要執(zhí)行的重要參數(shù)。分析到這里,實(shí)現(xiàn)異常處理的大致思路就開始明朗了,我們可以參考RibbonRoutingFilter的實(shí)現(xiàn)對(duì)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; 

通過(guò)上面的改造之后,我們?cè)賴L試訪問(wèn)之前的接口,這個(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過(guò)濾器正常處理并返回給客戶端了,同時(shí)在網(wǎng)關(guān)的控制臺(tái)中也輸出了異常信息。從返回的響應(yīng)信息中,我們可以看到幾個(gè)我們之前設(shè)置在請(qǐng)求上下文中的內(nèi)容,它們的對(duì)應(yīng)關(guān)系如下:

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

解決方案二:ErrorFilter處理

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

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

 
 
 
  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.     } 

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

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

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


名稱欄目:SpringCloud實(shí)戰(zhàn)小貼士:Zuul統(tǒng)一異常處理(一)
URL分享:http://www.dlmjj.cn/article/cohcjjo.html