Spring Cloud源碼分析(四)Zuul:核心過濾器
通過前文的介紹,我們對于Zuul的***印象通常是這樣的:它包含了對請求的路由和過濾兩個功能,其中路由功能負(fù)責(zé)將外部請求轉(zhuǎn)發(fā)到具體的微服務(wù)實例上,是實現(xiàn)外部訪問統(tǒng)一入口的基礎(chǔ);而過濾器功能則負(fù)責(zé)對請求的處理過程進行干預(yù),是實現(xiàn)請求校驗、服務(wù)聚合等功能的基礎(chǔ)。然而實際上,路由功能在真正運行時,它的路由映射和請求轉(zhuǎn)發(fā)都是由幾個不同的過濾器完成的。其中,路由映射主要通過pre類型的過濾器完成,它將請求路徑與配置的路由規(guī)則進行匹配,以找到需要轉(zhuǎn)發(fā)的目標(biāo)地址;而請求轉(zhuǎn)發(fā)的部分則是由route類型的過濾器來完成,對pre類型過濾器獲得的路由地址進行轉(zhuǎn)發(fā)。所以,過濾器可以說是Zuul實現(xiàn)API網(wǎng)關(guān)功能最為核心的部件,每一個進入Zuul的HTTP請求都會經(jīng)過一系列的過濾器處理鏈得到請求響應(yīng)并返回給客戶端。
下面,我們就通過本文來詳細(xì)了解一下Spring Cloud Zuul的過濾器!以下內(nèi)容節(jié)選自《Spring Cloud微服務(wù)實戰(zhàn)》,稍做加工。
一、過濾器
在Spring Cloud Zuul中實現(xiàn)的過濾器必須包含4個基本特征:過濾類型、執(zhí)行順序、執(zhí)行條件、具體操作。這些元素看著似乎非常的熟悉,實際上它就是ZuulFilter接口中定義的四個抽象方法:
- String filterType();
 - int filterOrder();
 - boolean shouldFilter();
 - Object run();
 
它們各自的含義與功能總結(jié)如下:
filterType:該函數(shù)需要返回一個字符串來代表過濾器的類型,而這個類型就是在HTTP請求過程中定義的各個階段。在Zuul中默認(rèn)定義了四種不同生命周期的過濾器類型,具體如下:
- pre:可以在請求被路由之前調(diào)用。
 - routing:在路由請求時候被調(diào)用。
 - post:在routing和error過濾器之后被調(diào)用。
 - error:處理請求時發(fā)生錯誤時被調(diào)用。
 
filterOrder:通過int值來定義過濾器的執(zhí)行順序,數(shù)值越小優(yōu)先級越高。
shouldFilter:返回一個boolean類型來判斷該過濾器是否要執(zhí)行。我們可以通過此方法來指定過濾器的有效范圍。
run:過濾器的具體邏輯。在該函數(shù)中,我們可以實現(xiàn)自定義的過濾邏輯,來確定是否要攔截當(dāng)前的請求,不對其進行后續(xù)的路由,或是在請求路由返回結(jié)果之后,對處理結(jié)果做一些加工等。
二、請求生命周期
上一節(jié)中,對于Spring Cloud Zuul中的過濾器類型filterType,我們已經(jīng)做過一些簡單的介紹,Zuul默認(rèn)定義了四個不同的過濾器類型,它們覆蓋了一個外部HTTP請求到達(dá)API網(wǎng)關(guān),直到返回請求結(jié)果的全部生命周期。下圖源自Zuul的官方WIKI中關(guān)于請求生命周期的圖解,它描述了一個HTTP請求到達(dá)API網(wǎng)關(guān)之后,如何在各個不同類型的過濾器之間流轉(zhuǎn)的詳細(xì)過程。
從上圖中,我們可以看到,當(dāng)外部HTTP請求到達(dá)API網(wǎng)關(guān)服務(wù)的時候,首先它會進入***個階段pre,在這里它會被pre類型的過濾器進行處理,該類型的過濾器主要目的是在進行請求路由之前做一些前置加工,比如請求的校驗等。在完成了pre類型的過濾器處理之后,請求進入第二個階段routing,也就是之前說的路由請求轉(zhuǎn)發(fā)階段,請求將會被routing類型過濾器處理,這里的具體處理內(nèi)容就是將外部請求轉(zhuǎn)發(fā)到具體服務(wù)實例上去的過程,當(dāng)服務(wù)實例將請求結(jié)果都返回之后,routing階段完成,請求進入第三個階段post,此時請求將會被post類型的過濾器進行處理,這些過濾器在處理的時候不僅可以獲取到請求信息,還能獲取到服務(wù)實例的返回信息,所以在post類型的過濾器中,我們可以對處理結(jié)果進行一些加工或轉(zhuǎn)換等內(nèi)容。另外,還有一個特殊的階段error,該階段只有在上述三個階段中發(fā)生異常的時候才會觸發(fā),但是它的***流向還是post類型的過濾器,因為它需要通過post過濾器將最終結(jié)果返回給請求客戶端(實際實現(xiàn)上還有一些差別,后續(xù)介紹)。
三、核心過濾器
在Spring Cloud Zuul中,為了讓API網(wǎng)關(guān)組件可以更方便的上手使用,它在HTTP請求生命周期的各個階段默認(rèn)地實現(xiàn)了一批核心過濾器,它們會在API網(wǎng)關(guān)服務(wù)啟動的時候被自動地加載和啟用。我們可以在源碼中查看和了解它們,它們定義于spring-cloud-netflix-core模塊的org.springframework.cloud.netflix.zuul.filters包下。
如上圖所示,在默認(rèn)啟用的過濾器中包含了三種不同生命周期的過濾器,這些過濾器都非常重要,可以幫助我們理解Zuul對外部請求處理的過程,以及幫助我們?nèi)绾卧诖嘶A(chǔ)上擴展過濾器去完成自身系統(tǒng)需要的功能。下面,我們將逐個地對這些過濾器做一些詳細(xì)的介紹:
1. pre過濾器
ServletDetectionFilter:它的執(zhí)行順序為-3,是***被執(zhí)行的過濾器。該過濾器總是會被執(zhí)行,主要用來檢測當(dāng)前請求是通過Spring的DispatcherServlet處理運行,還是通過ZuulServlet來處理運行的。它的檢測結(jié)果會以布爾類型保存在當(dāng)前請求上下文的isDispatcherServletRequest參數(shù)中,這樣在后續(xù)的過濾器中,我們就可以通過RequestUtils.isDispatcherServletRequest()和RequestUtils.isZuulServletRequest()方法判斷它以實現(xiàn)做不同的處理。一般情況下,發(fā)送到API網(wǎng)關(guān)的外部請求都會被Spring的DispatcherServlet處理,除了通過/zuul/路徑訪問的請求會繞過DispatcherServlet,被ZuulServlet處理,主要用來應(yīng)對處理大文件上傳的情況。另外,對于ZuulServlet的訪問路徑/zuul/,我們可以通過zuul.servletPath參數(shù)來進行修改。
- Servlet30WrapperFilter:它的執(zhí)行順序為-2,是第二個執(zhí)行的過濾器。目前的實現(xiàn)會對所有請求生效,主要為了將原始的HttpServletRequest包裝成Servlet30RequestWrapper對象。
 
- FormBodyWrapperFilter:它的執(zhí)行順序為-1,是第三個執(zhí)行的過濾器。該過濾器僅對兩種類請求生效,***類是Content-Type為application/x-www-form-urlencoded的請求,第二類是Content-Type為multipart/form-data并且是由Spring的DispatcherServlet處理的請求(用到了ServletDetectionFilter的處理結(jié)果)。而該過濾器的主要目的是將符合要求的請求體包裝成FormBodyRequestWrapper對象。
 
- DebugFilter:它的執(zhí)行順序為1,是第四個執(zhí)行的過濾器。該過濾器會根據(jù)配置參數(shù)zuul.debug.request和請求中的debug參數(shù)來決定是否執(zhí)行過濾器中的操作。而它的具體操作內(nèi)容則是將當(dāng)前的請求上下文中的debugRouting和debugRequest參數(shù)設(shè)置為true。由于在同一個請求的不同生命周期中,都可以訪問到這兩個值,所以我們在后續(xù)的各個過濾器中可以利用這兩值來定義一些debug信息,這樣當(dāng)線上環(huán)境出現(xiàn)問題的時候,可以通過請求參數(shù)的方式來激活這些debug信息以幫助分析問題。另外,對于請求參數(shù)中的debug參數(shù),我們也可以通過zuul.debug.parameter來進行自定義。
 
- PreDecorationFilter:它的執(zhí)行順序為5,是pre階段***被執(zhí)行的過濾器。該過濾器會判斷當(dāng)前請求上下文中是否存在forward.to和serviceId參數(shù),如果都不存在,那么它就會執(zhí)行具體過濾器的操作(如果有一個存在的話,說明當(dāng)前請求已經(jīng)被處理過了,因為這兩個信息就是根據(jù)當(dāng)前請求的路由信息加載進來的)。而它的具體操作內(nèi)容就是為當(dāng)前請求做一些預(yù)處理,比如:進行路由規(guī)則的匹配、在請求上下文中設(shè)置該請求的基本信息以及將路由匹配結(jié)果等一些設(shè)置信息等,這些信息將是后續(xù)過濾器進行處理的重要依據(jù),我們可以通過RequestContext.getCurrentContext()來訪問這些信息。另外,我們還可以在該實現(xiàn)中找到一些對HTTP頭請求進行處理的邏輯,其中包含了一些耳熟能詳?shù)念^域,比如:X-Forwarded-Host、X-Forwarded-Port。另外,對于這些頭域的記錄是通過zuul.addProxyHeaders參數(shù)進行控制的,而這個參數(shù)默認(rèn)值為true,所以Zuul在請求跳轉(zhuǎn)時默認(rèn)地會為請求增加X-Forwarded-*頭域,包括:X-Forwarded-Host、X-Forwarded-Port、X-Forwarded-For、X-Forwarded-Prefix、X-Forwarded-Proto。我們也可以通過設(shè)置zuul.addProxyHeaders=false關(guān)閉對這些頭域的添加動作。
 
《Spring Cloud實戰(zhàn)小貼士:Zuul處理Cookie和重定向》 一文中提到的加載敏感頭信息加入到忽略頭信息的操作調(diào)用就在PreDecorationFilter過濾器中實現(xiàn)。
2. route過濾器
- RibbonRoutingFilter:它的執(zhí)行順序為10,是route階段***個執(zhí)行的過濾器。該過濾器只對請求上下文中存在serviceId參數(shù)的請求進行處理,即只對通過serviceId配置路由規(guī)則的請求生效。而該過濾器的執(zhí)行邏輯就是面向服務(wù)路由的核心,它通過使用Ribbon和Hystrix來向服務(wù)實例發(fā)起請求,并將服務(wù)實例的請求結(jié)果返回。
 
- SimpleHostRoutingFilter:它的執(zhí)行順序為100,是route階段第二個執(zhí)行的過濾器。該過濾器只對請求上下文中存在routeHost參數(shù)的請求進行處理,即只對通過url配置路由規(guī)則的請求生效。而該過濾器的執(zhí)行邏輯就是直接向routeHost參數(shù)的物理地址發(fā)起請求,從源碼中我們可以知道該請求是直接通過httpclient包實現(xiàn)的,而沒有使用Hystrix命令進行包裝,所以這類請求并沒有線程隔離和斷路器的保護。
 
- SendForwardFilter:它的執(zhí)行順序為500,是route階段第三個執(zhí)行的過濾器。該過濾器只對請求上下文中存在forward.to參數(shù)的請求進行處理,即用來處理路由規(guī)則中的forward本地跳轉(zhuǎn)配置。
 
3. post過濾器
- SendErrorFilter:它的執(zhí)行順序為0,是post階段***個執(zhí)行的過濾器。該過濾器僅在請求上下文中包含error.status_code參數(shù)(由之前執(zhí)行的過濾器設(shè)置的錯誤編碼)并且還沒有被該過濾器處理過的時候執(zhí)行。而該過濾器的具體邏輯就是利用請求上下文中的錯誤信息來組織成一個forward到API網(wǎng)關(guān)/error錯誤端點的請求來產(chǎn)生錯誤響應(yīng)。
 
- SendResponseFilter:它的執(zhí)行順序為1000,是post階段***執(zhí)行的過濾器。該過濾器會檢查請求上下文中是否包含請求響應(yīng)相關(guān)的頭信息、響應(yīng)數(shù)據(jù)流或是響應(yīng)體,只有在包含它們其中一個的時候就會執(zhí)行處理邏輯。而該過濾器的處理邏輯就是利用請求上下文的響應(yīng)信息來組織需要發(fā)送回客戶端的響應(yīng)內(nèi)容。
 
這里不列出具體代碼了,讀者可自行根據(jù)類名來查看源碼了解詳細(xì)處理過程。下圖是對上述過濾器根據(jù)順序、名稱、功能、類型做了綜合的整理,可以幫助我們在自定義過濾器或是擴展過濾器的時候用來參考并全面地考慮整個請求生命周期的處理過程。
【本文為51CTO專欄作者“翟永超”的原創(chuàng)稿件,轉(zhuǎn)載請通過51CTO聯(lián)系作者獲取授權(quán)】


















 
 
 









 
 
 
 