Spring Cloud實(shí)戰(zhàn)小貼士:Zuul統(tǒng)一異常處理(三)
本篇作為《Spring Cloud微服務(wù)實(shí)戰(zhàn)》一書關(guān)于Spring Cloud Zuul網(wǎng)關(guān)在Dalston版本對異常處理的補(bǔ)充。沒有看過本書的讀書也不要緊,可以先閱讀我之前的兩篇博文:《Spring Cloud實(shí)戰(zhàn)小貼士:Zuul統(tǒng)一異常處理(一)》和《Spring Cloud實(shí)戰(zhàn)小貼士:Zuul統(tǒng)一異常處理(二)》,這兩篇文章都詳細(xì)介紹和分析了Spring Cloud Zuul在過濾器設(shè)計(jì)中對異常處理的不足。同時(shí),在這兩篇文章中,也針對不足之處做了相應(yīng)的解決方案。不過,這些方案都是基于Brixton版本所做的,在***的Dalston版本中,Spring Cloud Zuul做了一些優(yōu)化,所以我們不再需要做這些擴(kuò)展就已經(jīng)能夠正確的處理異常信息了。那么,在Dalston版中,Spring Cloud Zuul中做了怎么樣的修改以達(dá)到之前我們自己擴(kuò)展的效果呢?
過濾器類型的變更
讀者是否還記得我們之前分析了Spring Cloud Zuul自帶的核心過濾器有哪些呢?我們先根據(jù)下圖回憶一下:
這次主要將SendErrorFilter過濾器的類型從POST改為了ERROR,所以核心過濾器變成了如下圖的結(jié)構(gòu):
處理邏輯的變化
既然過濾器類型發(fā)生了變化,那么請求的處理生命周期就會(huì)有所變化。在變化之前,各階段過濾器的流轉(zhuǎn)如下圖所示:
針對異常情況,在圖中我們標(biāo)出了不同的顏色。從pre和route階段拋出的異常將會(huì)進(jìn)入error階段,再進(jìn)入到post階段進(jìn)行返回。由于SendErrorFilter需要判斷請求上下文中是否包含error.status_code屬性:有的話就用SendErrorFilter處理錯(cuò)誤結(jié)果;沒有的話就用SendResponseFilter返回正常結(jié)果,但是error.status_code屬性默認(rèn)是在各個(gè)階段過濾器中自己put進(jìn)去的,這就導(dǎo)致,各個(gè)階段過濾器拋出異常之后,是沒有辦法返回錯(cuò)誤結(jié)果的。因此,我們擴(kuò)展了一個(gè)ErrorFilter來捕獲異常,然后手工的設(shè)置error.status_code屬性,讓SendErrorFilter能正常運(yùn)作。
通過上面你的改造,從pre和route階段的異常都能處理了,但是post階段拋出異常后,是不會(huì)再進(jìn)入post階段的,這使得ErrorFilter設(shè)置了設(shè)置error.status_code屬性之后,也沒有過濾器去組織返回結(jié)果,所以我們通過繼承SendErrorFilter在error階段增加了一個(gè)返回錯(cuò)誤信息的過濾器。
而這次在Dalston版本中,做了很巧妙的變動(dòng):就是上文所述的對SendErrorFilter過濾器類型的變更,這一變動(dòng)使得所有階段的異常都會(huì)被SendErrorFilter處理,直接解決的上面的第二個(gè)問題。當(dāng)然只是做個(gè)變動(dòng)還是不夠的,為了區(qū)分SendErrorFilter和SendResponseFitler分別處理出現(xiàn)異常和未出現(xiàn)異常的情況,修改原來根據(jù)error.status_code屬性判斷的邏輯,而是改為根據(jù)請求上下文中是否包含Throwable來作為基本依據(jù),而這個(gè)對象是在過濾器出現(xiàn)異常之后,Zuul往請求上下文中置入的,所以可以更為準(zhǔn)確的判斷當(dāng)前請求處理是否出現(xiàn)了異常,而不再需要我們之前擴(kuò)展的ErrorFilter了。
- public class SendErrorFilter extends ZuulFilter {
- @Override
- public boolean shouldFilter() {
- RequestContext ctx = RequestContext.getCurrentContext();
- return ctx.containsKey("error.status_code")
- && !ctx.getBoolean(SEND_ERROR_FILTER_RAN, false);
- }
- ...
- }
- public class SendResponseFilter extends ZuulFilter {
- @Override
- public boolean shouldFilter() {
- RequestContext context = RequestContext.getCurrentContext();
- return context.getThrowable() == null
- && (!context.getZuulResponseHeaders().isEmpty()
- || context.getResponseDataStream() != null
- || context.getResponseBody() != null);
- }
- ...
- }
所以,***修改之后,整個(gè)處理邏輯變?yōu)槿缦聢D所示的流程:
【本文為51CTO專欄作者“翟永超”的原創(chuàng)稿件,轉(zhuǎn)載請通過51CTO聯(lián)系作者獲取授權(quán)】