Spring Cloud源碼分析(四)Zuul:核心過濾器

通過之前發(fā)布的《Spring Cloud構(gòu)建微服務(wù)架構(gòu)(五)服務(wù)網(wǎng)關(guān)》一文撬码,相信大家對于Spring Cloud Zuul已經(jīng)有了一個(gè)基礎(chǔ)的認(rèn)識(shí)。通過前文的介紹拢切,我們對于Zuul的第一印象通常是這樣的:它包含了對請求的路由和過濾兩個(gè)功能,其中路由功能負(fù)責(zé)將外部請求轉(zhuǎn)發(fā)到具體的微服務(wù)實(shí)例上葡缰,是實(shí)現(xiàn)外部訪問統(tǒng)一入口的基礎(chǔ)淆衷;而過濾器功能則負(fù)責(zé)對請求的處理過程進(jìn)行干預(yù)颠放,是實(shí)現(xiàn)請求校驗(yàn)、服務(wù)聚合等功能的基礎(chǔ)吭敢。然而實(shí)際上,路由功能在真正運(yùn)行時(shí)暮芭,它的路由映射和請求轉(zhuǎn)發(fā)都是由幾個(gè)不同的過濾器完成的鹿驼。其中,路由映射主要通過pre類型的過濾器完成辕宏,它將請求路徑與配置的路由規(guī)則進(jìn)行匹配畜晰,以找到需要轉(zhuǎn)發(fā)的目標(biāo)地址;而請求轉(zhuǎn)發(fā)的部分則是由route類型的過濾器來完成瑞筐,對pre類型過濾器獲得的路由地址進(jìn)行轉(zhuǎn)發(fā)凄鼻。所以,過濾器可以說是Zuul實(shí)現(xiàn)API網(wǎng)關(guān)功能最為核心的部件聚假,每一個(gè)進(jìn)入Zuul的HTTP請求都會(huì)經(jīng)過一系列的過濾器處理鏈得到請求響應(yīng)并返回給客戶端块蚌。

下面,我們就通過本文來詳細(xì)了解一下Spring Cloud Zuul的過濾器膘格!以下內(nèi)容節(jié)選自《Spring Cloud微服務(wù)實(shí)戰(zhàn)》,稍做加工。

過濾器

在Spring Cloud Zuul中實(shí)現(xiàn)的過濾器必須包含4個(gè)基本特征:過濾類型痴脾、執(zhí)行順序牙言、執(zhí)行條件、具體操作菜秦。這些元素看著似乎非常的熟悉甜害,實(shí)際上它就是ZuulFilter接口中定義的四個(gè)抽象方法:

String filterType();
    
int filterOrder();
    
boolean shouldFilter();
    
Object run();

它們各自的含義與功能總結(jié)如下:

  • filterType:該函數(shù)需要返回一個(gè)字符串來代表過濾器的類型,而這個(gè)類型就是在HTTP請求過程中定義的各個(gè)階段球昨。在Zuul中默認(rèn)定義了四種不同生命周期的過濾器類型尔店,具體如下:
    • pre:可以在請求被路由之前調(diào)用。
    • routing:在路由請求時(shí)候被調(diào)用褪尝。
    • post:在routing和error過濾器之后被調(diào)用闹获。
    • error:處理請求時(shí)發(fā)生錯(cuò)誤時(shí)被調(diào)用。
  • filterOrder:通過int值來定義過濾器的執(zhí)行順序河哑,數(shù)值越小優(yōu)先級(jí)越高避诽。
  • shouldFilter:返回一個(gè)boolean類型來判斷該過濾器是否要執(zhí)行。我們可以通過此方法來指定過濾器的有效范圍璃谨。
  • run:過濾器的具體邏輯沙庐。在該函數(shù)中鲤妥,我們可以實(shí)現(xiàn)自定義的過濾邏輯,來確定是否要攔截當(dāng)前的請求拱雏,不對其進(jìn)行后續(xù)的路由棉安,或是在請求路由返回結(jié)果之后,對處理結(jié)果做一些加工等铸抑。

請求生命周期

上一節(jié)中贡耽,對于Spring Cloud Zuul中的過濾器類型filterType,我們已經(jīng)做過一些簡單的介紹鹊汛,Zuul默認(rèn)定義了四個(gè)不同的過濾器類型蒲赂,它們覆蓋了一個(gè)外部HTTP請求到達(dá)API網(wǎng)關(guān),直到返回請求結(jié)果的全部生命周期刁憋。下圖源自Zuul的官方WIKI中關(guān)于請求生命周期的圖解滥嘴,它描述了一個(gè)HTTP請求到達(dá)API網(wǎng)關(guān)之后,如何在各個(gè)不同類型的過濾器之間流轉(zhuǎn)的詳細(xì)過程至耻。

請求生命周期官方圖解

從上圖中若皱,我們可以看到,當(dāng)外部HTTP請求到達(dá)API網(wǎng)關(guān)服務(wù)的時(shí)候尘颓,首先它會(huì)進(jìn)入第一個(gè)階段pre走触,在這里它會(huì)被pre類型的過濾器進(jìn)行處理,該類型的過濾器主要目的是在進(jìn)行請求路由之前做一些前置加工疤苹,比如請求的校驗(yàn)等饺汹。在完成了pre類型的過濾器處理之后,請求進(jìn)入第二個(gè)階段routing痰催,也就是之前說的路由請求轉(zhuǎn)發(fā)階段兜辞,請求將會(huì)被routing類型過濾器處理,這里的具體處理內(nèi)容就是將外部請求轉(zhuǎn)發(fā)到具體服務(wù)實(shí)例上去的過程夸溶,當(dāng)服務(wù)實(shí)例將請求結(jié)果都返回之后逸吵,routing階段完成,請求進(jìn)入第三個(gè)階段post缝裁,此時(shí)請求將會(huì)被post類型的過濾器進(jìn)行處理扫皱,這些過濾器在處理的時(shí)候不僅可以獲取到請求信息,還能獲取到服務(wù)實(shí)例的返回信息捷绑,所以在post類型的過濾器中韩脑,我們可以對處理結(jié)果進(jìn)行一些加工或轉(zhuǎn)換等內(nèi)容。另外粹污,還有一個(gè)特殊的階段error段多,該階段只有在上述三個(gè)階段中發(fā)生異常的時(shí)候才會(huì)觸發(fā),但是它的最后流向還是post類型的過濾器壮吩,因?yàn)樗枰ㄟ^post過濾器將最終結(jié)果返回給請求客戶端(實(shí)際實(shí)現(xiàn)上還有一些差別进苍,后續(xù)介紹)加缘。

核心過濾器

在Spring Cloud Zuul中,為了讓API網(wǎng)關(guān)組件可以更方便的上手使用觉啊,它在HTTP請求生命周期的各個(gè)階段默認(rèn)地實(shí)現(xiàn)了一批核心過濾器拣宏,它們會(huì)在API網(wǎng)關(guān)服務(wù)啟動(dòng)的時(shí)候被自動(dòng)地加載和啟用。我們可以在源碼中查看和了解它們杠人,它們定義于spring-cloud-netflix-core模塊的org.springframework.cloud.netflix.zuul.filters包下勋乾。

默認(rèn)實(shí)現(xiàn)的核心過濾器

如上圖所示,在默認(rèn)啟用的過濾器中包含了三種不同生命周期的過濾器嗡善,這些過濾器都非常重要市俊,可以幫助我們理解Zuul對外部請求處理的過程,以及幫助我們?nèi)绾卧诖嘶A(chǔ)上擴(kuò)展過濾器去完成自身系統(tǒng)需要的功能滤奈。下面,我們將逐個(gè)地對這些過濾器做一些詳細(xì)的介紹:

pre過濾器

  • ServletDetectionFilter:它的執(zhí)行順序?yàn)?3撩满,是最先被執(zhí)行的過濾器蜒程。該過濾器總是會(huì)被執(zhí)行,主要用來檢測當(dāng)前請求是通過Spring的DispatcherServlet處理運(yùn)行伺帘,還是通過ZuulServlet來處理運(yùn)行的昭躺。它的檢測結(jié)果會(huì)以布爾類型保存在當(dāng)前請求上下文的isDispatcherServletRequest參數(shù)中,這樣在后續(xù)的過濾器中伪嫁,我們就可以通過RequestUtils.isDispatcherServletRequest()和RequestUtils.isZuulServletRequest()方法判斷它以實(shí)現(xiàn)做不同的處理领炫。一般情況下,發(fā)送到API網(wǎng)關(guān)的外部請求都會(huì)被Spring的DispatcherServlet處理张咳,除了通過/zuul/路徑訪問的請求會(huì)繞過DispatcherServlet帝洪,被ZuulServlet處理,主要用來應(yīng)對處理大文件上傳的情況脚猾。另外葱峡,對于ZuulServlet的訪問路徑/zuul/,我們可以通過zuul.servletPath參數(shù)來進(jìn)行修改龙助。
  • Servlet30WrapperFilter:它的執(zhí)行順序?yàn)?2砰奕,是第二個(gè)執(zhí)行的過濾器。目前的實(shí)現(xiàn)會(huì)對所有請求生效提鸟,主要為了將原始的HttpServletRequest包裝成Servlet30RequestWrapper對象军援。
  • FormBodyWrapperFilter:它的執(zhí)行順序?yàn)?1,是第三個(gè)執(zhí)行的過濾器称勋。該過濾器僅對兩種類請求生效胸哥,第一類是Content-Type為application/x-www-form-urlencoded的請求,第二類是Content-Type為multipart/form-data并且是由Spring的DispatcherServlet處理的請求(用到了ServletDetectionFilter的處理結(jié)果)赡鲜。而該過濾器的主要目的是將符合要求的請求體包裝成FormBodyRequestWrapper對象烘嘱。
  • DebugFilter:它的執(zhí)行順序?yàn)?昆禽,是第四個(gè)執(zhí)行的過濾器。該過濾器會(huì)根據(jù)配置參數(shù)zuul.debug.request和請求中的debug參數(shù)來決定是否執(zhí)行過濾器中的操作蝇庭。而它的具體操作內(nèi)容則是將當(dāng)前的請求上下文中的debugRouting和debugRequest參數(shù)設(shè)置為true醉鳖。由于在同一個(gè)請求的不同生命周期中,都可以訪問到這兩個(gè)值哮内,所以我們在后續(xù)的各個(gè)過濾器中可以利用這兩值來定義一些debug信息盗棵,這樣當(dāng)線上環(huán)境出現(xiàn)問題的時(shí)候,可以通過請求參數(shù)的方式來激活這些debug信息以幫助分析問題北发。另外纹因,對于請求參數(shù)中的debug參數(shù),我們也可以通過zuul.debug.parameter來進(jìn)行自定義琳拨。
  • PreDecorationFilter:它的執(zhí)行順序?yàn)?瞭恰,是pre階段最后被執(zhí)行的過濾器。該過濾器會(huì)判斷當(dāng)前請求上下文中是否存在forward.to和serviceId參數(shù)狱庇,如果都不存在惊畏,那么它就會(huì)執(zhí)行具體過濾器的操作(如果有一個(gè)存在的話,說明當(dāng)前請求已經(jīng)被處理過了密任,因?yàn)檫@兩個(gè)信息就是根據(jù)當(dāng)前請求的路由信息加載進(jìn)來的)颜启。而它的具體操作內(nèi)容就是為當(dāng)前請求做一些預(yù)處理,比如:進(jìn)行路由規(guī)則的匹配浪讳、在請求上下文中設(shè)置該請求的基本信息以及將路由匹配結(jié)果等一些設(shè)置信息等缰盏,這些信息將是后續(xù)過濾器進(jìn)行處理的重要依據(jù),我們可以通過RequestContext.getCurrentContext()來訪問這些信息淹遵。另外口猜,我們還可以在該實(shí)現(xiàn)中找到一些對HTTP頭請求進(jìn)行處理的邏輯,其中包含了一些耳熟能詳?shù)念^域透揣,比如:X-Forwarded-Host暮的、X-Forwarded-Port。另外淌实,對于這些頭域的記錄是通過zuul.addProxyHeaders參數(shù)進(jìn)行控制的冻辩,而這個(gè)參數(shù)默認(rèn)值為true,所以Zuul在請求跳轉(zhuǎn)時(shí)默認(rèn)地會(huì)為請求增加X-Forwarded-*頭域拆祈,包括:X-Forwarded-Host恨闪、X-Forwarded-Port、X-Forwarded-For放坏、X-Forwarded-Prefix咙咽、X-Forwarded-Proto。我們也可以通過設(shè)置zuul.addProxyHeaders=false關(guān)閉對這些頭域的添加動(dòng)作淤年。

《Spring Cloud實(shí)戰(zhàn)小貼士:Zuul處理Cookie和重定向》 一文中提到的加載敏感頭信息加入到忽略頭信息的操作調(diào)用就在PreDecorationFilter過濾器中實(shí)現(xiàn)钧敞。

route過濾器

  • RibbonRoutingFilter:它的執(zhí)行順序?yàn)?0蜡豹,是route階段第一個(gè)執(zhí)行的過濾器。該過濾器只對請求上下文中存在serviceId參數(shù)的請求進(jìn)行處理溉苛,即只對通過serviceId配置路由規(guī)則的請求生效镜廉。而該過濾器的執(zhí)行邏輯就是面向服務(wù)路由的核心,它通過使用Ribbon和Hystrix來向服務(wù)實(shí)例發(fā)起請求愚战,并將服務(wù)實(shí)例的請求結(jié)果返回娇唯。
  • SimpleHostRoutingFilter:它的執(zhí)行順序?yàn)?00,是route階段第二個(gè)執(zhí)行的過濾器寂玲。該過濾器只對請求上下文中存在routeHost參數(shù)的請求進(jìn)行處理塔插,即只對通過url配置路由規(guī)則的請求生效。而該過濾器的執(zhí)行邏輯就是直接向routeHost參數(shù)的物理地址發(fā)起請求拓哟,從源碼中我們可以知道該請求是直接通過httpclient包實(shí)現(xiàn)的想许,而沒有使用Hystrix命令進(jìn)行包裝,所以這類請求并沒有線程隔離和斷路器的保護(hù)断序。
  • SendForwardFilter:它的執(zhí)行順序?yàn)?00流纹,是route階段第三個(gè)執(zhí)行的過濾器。該過濾器只對請求上下文中存在forward.to參數(shù)的請求進(jìn)行處理逢倍,即用來處理路由規(guī)則中的forward本地跳轉(zhuǎn)配置。

post過濾器

  • SendErrorFilter:它的執(zhí)行順序?yàn)?景图,是post階段第一個(gè)執(zhí)行的過濾器较雕。該過濾器僅在請求上下文中包含error.status_code參數(shù)(由之前執(zhí)行的過濾器設(shè)置的錯(cuò)誤編碼)并且還沒有被該過濾器處理過的時(shí)候執(zhí)行。而該過濾器的具體邏輯就是利用請求上下文中的錯(cuò)誤信息來組織成一個(gè)forward到API網(wǎng)關(guān)/error錯(cuò)誤端點(diǎn)的請求來產(chǎn)生錯(cuò)誤響應(yīng)挚币。
  • SendResponseFilter:它的執(zhí)行順序?yàn)?000亮蒋,是post階段最后執(zhí)行的過濾器。該過濾器會(huì)檢查請求上下文中是否包含請求響應(yīng)相關(guān)的頭信息妆毕、響應(yīng)數(shù)據(jù)流或是響應(yīng)體慎玖,只有在包含它們其中一個(gè)的時(shí)候就會(huì)執(zhí)行處理邏輯。而該過濾器的處理邏輯就是利用請求上下文的響應(yīng)信息來組織需要發(fā)送回客戶端的響應(yīng)內(nèi)容笛粘。

這里不列出具體代碼了趁怔,讀者可自行根據(jù)類名來查看源碼了解詳細(xì)處理過程。下圖是對上述過濾器根據(jù)順序薪前、名稱润努、功能、類型做了綜合的整理示括,可以幫助我們在自定義過濾器或是擴(kuò)展過濾器的時(shí)候用來參考并全面地考慮整個(gè)請求生命周期的處理過程铺浇。

核心過濾器總結(jié)

本文節(jié)選自《Spring Cloud微服務(wù)實(shí)戰(zhàn)》,部分稍做加工垛膝,轉(zhuǎn)載請注明出處

本文由 程序猿DD-翟永超 創(chuàng)作鳍侣,采用 CC BY 3.0 CN協(xié)議 進(jìn)行許可丁稀。 可自由轉(zhuǎn)載、引用倚聚,但需署名作者且注明文章出處线衫。
原文首發(fā)于:http://blog.didispace.com/spring-cloud-source-zuul/

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市秉沼,隨后出現(xiàn)的幾起案子桶雀,更是在濱河造成了極大的恐慌,老刑警劉巖唬复,帶你破解...
    沈念sama閱讀 216,544評論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件矗积,死亡現(xiàn)場離奇詭異,居然都是意外死亡敞咧,警方通過查閱死者的電腦和手機(jī)棘捣,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,430評論 3 392
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來休建,“玉大人乍恐,你說我怎么就攤上這事〔馍埃” “怎么了茵烈?”我有些...
    開封第一講書人閱讀 162,764評論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長砌些。 經(jīng)常有香客問我呜投,道長,這世上最難降的妖魔是什么存璃? 我笑而不...
    開封第一講書人閱讀 58,193評論 1 292
  • 正文 為了忘掉前任仑荐,我火速辦了婚禮,結(jié)果婚禮上纵东,老公的妹妹穿的比我還像新娘粘招。我一直安慰自己,他們只是感情好偎球,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,216評論 6 388
  • 文/花漫 我一把揭開白布洒扎。 她就那樣靜靜地躺著,像睡著了一般衰絮。 火紅的嫁衣襯著肌膚如雪逊笆。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,182評論 1 299
  • 那天岂傲,我揣著相機(jī)與錄音难裆,去河邊找鬼。 笑死,一個(gè)胖子當(dāng)著我的面吹牛乃戈,可吹牛的內(nèi)容都是我干的褂痰。 我是一名探鬼主播,決...
    沈念sama閱讀 40,063評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼症虑,長吁一口氣:“原來是場噩夢啊……” “哼缩歪!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起谍憔,我...
    開封第一講書人閱讀 38,917評論 0 274
  • 序言:老撾萬榮一對情侶失蹤匪蝙,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后习贫,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體逛球,經(jīng)...
    沈念sama閱讀 45,329評論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,543評論 2 332
  • 正文 我和宋清朗相戀三年苫昌,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了颤绕。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,722評論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡祟身,死狀恐怖奥务,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情袜硫,我是刑警寧澤氯葬,帶...
    沈念sama閱讀 35,425評論 5 343
  • 正文 年R本政府宣布,位于F島的核電站婉陷,受9級(jí)特大地震影響帚称,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜憨攒,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,019評論 3 326
  • 文/蒙蒙 一世杀、第九天 我趴在偏房一處隱蔽的房頂上張望阀参。 院中可真熱鬧肝集,春花似錦、人聲如沸蛛壳。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,671評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽衙荐。三九已至捞挥,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間忧吟,已是汗流浹背砌函。 一陣腳步聲響...
    開封第一講書人閱讀 32,825評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人讹俊。 一個(gè)月前我還...
    沈念sama閱讀 47,729評論 2 368
  • 正文 我出身青樓垦沉,卻偏偏與公主長得像,于是被迫代替她去往敵國和親仍劈。 傳聞我的和親對象是個(gè)殘疾皇子厕倍,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,614評論 2 353

推薦閱讀更多精彩內(nèi)容

  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理,服務(wù)發(fā)現(xiàn)贩疙,斷路器讹弯,智...
    卡卡羅2017閱讀 134,652評論 18 139
  • 學(xué)習(xí)是一個(gè)深入的過程,不停的反復(fù)的研究这溅,不能浮于表面组民,要深入原理。 之前我們了解到springcloud zuul...
    二月_春風(fēng)閱讀 48,284評論 6 22
  • Spring Boot 參考指南 介紹 轉(zhuǎn)載自:https://www.gitbook.com/book/qbgb...
    毛宇鵬閱讀 46,804評論 6 342
  • 每個(gè)月除了女生特有的那幾天外庇楞,我還會(huì)有另一個(gè)特殊的時(shí)期,那就是什么也不想去做否纬,什么也不愿意去想吕晌,而且很莫名其妙的煩...
    棗棗安閱讀 447評論 1 0
  • 是夜,那份孤獨(dú)再次降臨临燃,它仿若神靈的威壓睛驳,令我喘不過氣。 于是不再強(qiáng)定膜廊,索性推門而出乏沸,指意散心而去。 室外爪瓜,天氣頗...
    夜玗閱讀 267評論 0 5