springcloud系列—Zuul—第5章-3: Spring Cloud Zuul過濾器詳解
資料參考:《Spring Cloud 微服務實戰》
目錄
過濾器
在Spring Cloud Zuul 中實現的過濾器必須包含4個基本特徵:過濾型別、執行順序、執行條件、具體操作。這些元素實際上就算ZuulFilter介面中定義的4個抽象方法:
通過繼承ZuulFilter然後覆寫上面的4個方法,就可以實現一個簡單的過濾器,下面就相關注意點進行說明
1:filterType:返回一個字串代表過濾器的型別,在zuul中定義了四種不同生命週期的過濾器型別,具體如下:
pre
:可以在請求被路由之前呼叫route
:在路由請求時候被呼叫post
:在route和error過濾器之後被呼叫error
:處理請求時發生錯誤時被呼叫
Zuul的主要請求生命週期包括“pre”,“route”和“post”等階段。對於每個請求,都會執行具有這些型別的所有過濾器。
2:filterOrder
:通過int值來定義過濾器的執行順序
3:shouldFilter
:返回一個boolean型別來判斷該過濾器是否要執行,所以通過此函式可實現過濾器的開關。在上例中,我們直接返回true,所以該過濾器總是生效
4:run
:過濾器的具體邏輯。需要注意,這裡我們通過ctx.setSendZuulResponse(false)
令zuul過濾該請求,不對其進行路由,然後通過ctx.setResponseStatusCode(401)
設定了其返回的錯誤碼
請求生命週期
外部http請求到達api閘道器服務的時候:
- 首先它會進入第一個階段pre,在這裡它會被pre型別的過濾器進行處理。該型別過濾器的主要目的是在進行請求路由之前做一些前置加工,比如請求的校驗等。在完成了pre型別的過濾器處理之後,
- 請求進入第二個階段routing,也就是之前說的路由請求轉發階段,請求將會被routing型別的處理器處理。這裡的具體處理內容就是將外部請求轉發到具體服務例項上去的過程,當服務例項請求結果都返回之後,routing階段完成,
- 請求進入第三個階段post。此時請求將會被post型別的過濾器處理,這些過濾器在處理的時候不僅可以獲取到請求資訊,還能獲取到服務例項的返回資訊,所以在post型別的過濾器中,我們可以對處理結果進行一些加工或轉換等內容。
- 另外,還有一個特殊的階段error,該階段只有在上述三個階段中發生異常的時候才會觸發,但是它的最後流向還是post型別的過濾器,因為它需要通過post過濾器將最終結果返回給請求客戶端(對於error過濾器的處理,在spring cloud zuul的過濾鏈中實際上有一些不同)
核心過濾器
在spring cloud zuul
中,為了讓api閘道器元件可以被更方便的使用,它在http請求生命週期的各個階段預設實現了一批核心過濾器,它們會在api閘道器服務啟動的時候被自動載入和啟動。我們可以在原始碼中檢視和了解它們,它們定義與spring-cloud-netflix-core
模組的org.springframework.cloud.netflix.zuul.filters
包下。在預設啟動的過濾器中包含三種不同生命週期的過濾器,這些過濾器都非常重要,可以幫組我們理解zuul對外部請求處理的過程,以及幫助我們在此基礎上擴充套件過濾器去完成自身系統需要的功能。
pre過濾器
- ServletDetectionFilter:它的執行順序為-3,是最先被執行的過濾器。該過濾器總是會被執行,主要用來檢測當前請求是通過
Spring的DispatcherServlet
處理執行的,還是通過ZuulServlet
來處理執行的。它的檢測結果會以布林型別儲存在當前請求上下文的isDispatcherServletRequest
引數中,這樣後續的過濾器中,我們就可以通過RequestUtils.isDispatcherServletRequest()
和RequestUtils.isZuulServletRequest()
方法來判斷請求處理的源頭,以實現後續不同的處理機制。一般情況下,傳送到api閘道器的外部請求都會被Spring的DispatcherServlet
處理,除了通過/zuul/*
路徑訪問的請求會繞過DispatcherServlet
(比如之前我們說的大檔案上傳),被ZuulServlet
處理,主要用來應對大檔案上傳的情況。另外,對於ZuulServlet
的訪問路徑/zuul/*
,我們可以通過zuul.servletPath
引數進行修改。 - Servlet30WrapperFilter:它的執行順序為-2,是第二個執行的過濾器,目前的實現會對所有請求生效,主要為了將原始的
HttpServletRequest
包裝成Servlet30RequestWrapper
物件。 - FormBodyWrapperFilter:它的執行順序為-1,是第三個執行的過濾器。該過濾器僅對兩類請求生效,第一類是
Context-Type
為application/x-www-form-urlencoded
的請求,第二類是Context-Type為multipart/form-data
並且是由String的DispatcherServlet
處理的請求(用到了ServletDetectionFilter
的處理結果)。而該過濾器的主要目的是將符合要求的請求體包裝成FormBodyRequestWrapper
物件。 - DebugFilter:它的執行順序為1,是第四個執行的過濾器,該過濾器會根據配置引數
zuul.debug.request
和請求中的debug引數來決定是否執行過濾器中的操作。而它的具體操作內容是將當前請求上下文中的debugRouting
和debugRequest
引數設定為true。由於在同一個請求的不同生命週期都可以訪問到這二個值,所以我們在後續的各個過濾器中可以利用這二個值來定義一些debug資訊,這樣當線上環境出現問題的時候,可以通過引數的方式來啟用這些debug資訊以幫助分析問題,另外,對於請求引數中的debug引數,我們可以通過zuul.debug.parameter
來進行自定義。 - PreDecorationFilter:執行順序是5,是pre階段最後被執行的過濾器,該過濾器會判斷當前請求上下文中是否存在
forward.do
和serviceId
引數,如果都不存在,那麼它就會執行具體過濾器的操作(如果有一個存在的話,說明當前請求已經被處理過了,因為這二個資訊就是根據當前請求的路由資訊載入進來的)。而當它的具體操作內容就是為當前請求做一些預處理,比如說,進行路由規則的匹配,在請求上下文中設定該請求的基本資訊以及將路由匹配結果等一些設定資訊等,這些資訊將是後續過濾器進行處理的重要依據,我們可以通過RequestContext.getCurrentContext()
來訪問這些資訊。另外,我們還可以在該實現中找到對HTTP頭請求進行處理的邏輯,其中包含了一些耳熟能詳的頭域,比如X-Forwarded-Host
,X-Forwarded-Port
。另外,對於這些頭域是通過zuul.addProxyHeaders
引數進行控制的,而這個引數預設值是true,所以zuul在請求跳轉時預設會為請求增加X-Forwarded-*
頭域,包括X-Forwarded-Host
,X-Forwarded-Port
,X-Forwarded-For
,X-Forwarded-Prefix
,X-Forwarded-Proto
。也可以通過設定zuul.addProxyHeaders=false
關閉對這些頭域的新增動作。
route過濾器
- RibbonRoutingFilter:它的執行順序為10,是route階段的第一個執行的過濾器。該過濾器只對請求上下文中存在serviceId引數的請求進行處理,即只對通過serviceId配置路由規則的請求生效。而該過濾器的執行邏輯就是面向服務路由的核心,它通過使用ribbon和hystrix來向服務例項發起請求,並將服務例項的請求結果返回。
- SimpleHostRoutingFilter:它的執行順序為100,是route階段的第二個執行的過濾器。該過濾器只對請求上下文存在routeHost引數的請求進行處理,即只對通過url配置路由規則的請求生效。而該過濾器的執行邏輯就是直接向routeHost引數的實體地址發起請求,從原始碼中我們可以知道該請求是直接通過httpclient包實現的,而沒有使用Hystrix命令進行包裝,所以這類請求並沒有執行緒隔離和斷路器的保護。
- SendForwardFilter:它的執行順序是500,是route階段第三個執行的過濾器。該過濾器只對請求上下文中存在的
forward.do
引數進行處理請求,即用來處理路由規則中的forward本地跳轉裝配。
post過濾器
- SendErrorFilter:它的執行順序是0,是post階段的第一個執行的過濾器。該過濾器僅在請求上下文中包含
error.status_code
引數(由之前執行的過濾器設定的錯誤編碼)並且還沒有被該過濾器處理過的時候執行。而該過濾器的具體邏輯就是利用上下文中的錯誤資訊來組成一個forward
到api閘道器/error
錯誤端點的請求來產生錯誤響應。
- SendResponseFilter:它的執行順序為1000,是post階段最後執行的過濾器,該過濾器會檢查請求上下文中是否包含請求響應相關的頭資訊,響應資料流或是響應體,只有在包含它們其中一個的時候執行處理邏輯。而該過濾器的處理邏輯就是利用上下文的響應資訊來組織需要傳送回客戶端的響應內容。