Spring Cloud Gateway-全局過濾器

TIPS

本文基於Spring Cloud Gateway SR2,理論適配Spring Cloud Gateway SR1以及更高版本。

本文詳細探討Spring Cloud Gateway內置的全局過濾器。包括:

• Combined Global Filter and GatewayFilter Ordering • Forward Routing Filter • LoadBalancerClient Filter • Netty Routing Filter • Netty Write Response Filter • RouteToRequestUrl Filter • Websocket Routing Filter • Gateway Metrics Filter • Marking An Exchange As Routed

GlobalFilter 接口和 GatewayFilter 有一樣的接口定義,只不過, GlobalFilter 會作用於所有路由。

TIPS

官方聲明:GlobalFilter的接口定義以及用法在未來的版本可能會發生變化。

個人判斷:GlobalFilter可用於生產;如果有自定義GlobalFilter的需求,理論上也可放心使用——未來即使接口定義以及使用方式發生變化,應該也是平滑過渡的(比如Zuul的Fallback,原先叫ZuulFallbackProvider,後來改叫FallbackProvider,中間就有段時間新舊使用方式都支持,後面才逐步廢棄老的使用方式)。

1 Combined Global Filter and GatewayFilter Ordering

當請求到來時, Filtering Web Handler 處理器會添加所有 GlobalFilter 實例和匹配的 GatewayFilter 實例到過濾器鏈中。

過濾器鏈會使用 org.springframework.core.Ordered 註解所指定的順序,進行排序。Spring Cloud Gateway區分了過濾器邏輯執行的”pre”和”post”階段,所以優先級高的過濾器將會在pre階段最先執行,優先級最低的過濾器則在post階段最後執行。

TIPS

數值越小越靠前執行,記得這一點就OK了。

示例代碼:

@Bean@Order(-1)public GlobalFilter a() { return (exchange, chain) -> { log.info("first pre filter"); return chain.filter(exchange).then(Mono.fromRunnable(() -> { log.info("third post filter"); })); };}

@Bean@Order(0)public GlobalFilter b() { return (exchange, chain) -> { log.info("second pre filter"); return chain.filter(exchange).then(Mono.fromRunnable(() -> { log.info("second post filter"); })); };}

@Bean@Order(1)public GlobalFilter c() { return (exchange, chain) -> { log.info("third pre filter"); return chain.filter(exchange).then(Mono.fromRunnable(() -> { log.info("first post filter"); })); };}

執行結果:

first pre filtersecond pre filterthird pre filterfirst post filtersecond post filterthird post filter

2 Forward Routing Filter

ForwardRoutingFilter 會查看exchange的屬性 ServerWebExchangeUtils.GATEWAY_REQUEST_URL_ATTR 的值(一個URI),如果該值l的scheme是 forward ,比如: forward://localendpoint,則它會使用Spirng的 DispatcherHandler 處理該請求。請求URL的路徑部分,會被forward URL中的路徑覆蓋。未修改的原始URL,會被追加到 ServerWebExchangeUtils.GATEWAY_ORIGINAL_REQUEST_URL_ATTR 屬性中。

TIPS

這段文檔太學術了,講解了 LoadBalancerClientFilter 的實現原理,對使用者來說,意義不大;對使用者來說,只要知道這個Filter是用來做本地forward就OK了。

建議:如對原理感興趣的,建議直接研究源碼,源碼比官方文檔好理解。

3 LoadBalancerClient Filter

LoadBalancerClientFilter 會查看exchange的屬性 ServerWebExchangeUtils.GATEWAY_REQUEST_URL_ATTR 的值(一個URI),如果該值的scheme是 lb ,比如: lb://myservice ,它將會使用Spring Cloud的 LoadBalancerClient 來將 myservice 解析成實際的host和port,並替換掉 ServerWebExchangeUtils.GATEWAY_REQUEST_URL_ATTR 的內容。原始地址會追加到 ServerWebExchangeUtils.GATEWAY_ORIGINAL_REQUEST_URL_ATTR 中。該過濾器還會查看 ServerWebExchangeUtils.GATEWAY_SCHEME_PREFIX_ATTR 屬性,如果發現該屬性的值是 lb ,也會執行相同邏輯。

示例:

spring: cloud: gateway: routes: - id: myRoute uri: lb://service predicates: - Path=/service/**

默認情況下,如果無法在 LoadBalancer 找到指定服務的實例,那麼會返回503(對應如上的例子,找不到service實例,就返回503);可使用 spring.cloud.gateway.loadbalancer.use404=true 讓其返回404。

LoadBalancer 返回的 ServiceInstance 的 isSecure 的值,會覆蓋請求的scheme。舉個例子,如果請求打到Gateway上使用的是 HTTPS ,但 ServiceInstance 的 isSecure 是false,那麼下游收到的則是HTTP請求,反之亦然。然而,如果該路由指定了 GATEWAY_SCHEME_PREFIX_ATTR 屬性,那麼前綴將會被剝離,並且路由URL中的scheme會覆蓋 ServiceInstance 的配置

TIPS

這段文檔太學術了,講解了 LoadBalancerClientFilter 的實現原理,對使用者來說,意義不大; 對使用者來說,其實只要知道這個Filter是用來整合Ribbon的就OK了 。

建議:如對原理感興趣的,建議直接研究源碼,源碼比官方文檔好理解。

4 Netty Routing Filter

如果 ServerWebExchangeUtils.GATEWAY_REQUEST_URL_ATTR 的值的scheme是 http 或 https,則運行Netty Routing Filter 。它使用Netty HttpClient 向下遊發送代理請求。獲得的響應將放在exchange的 ServerWebExchangeUtils.CLIENT_RESPONSE_ATTR 屬性中,以便在後面的filter中使用。(有一個實驗性的過濾器: WebClientHttpRoutingFilter 可實現相同功能,但無需Netty)

5 Netty Write Response Filter

如果exchange中的 ServerWebExchangeUtils.CLIENT_RESPONSE_ATTR 屬性中有 HttpClientResponse ,則運行 NettyWriteResponseFilter 。該過濾器在所有其他過濾器執行完成後執行,並將代理響應協會網關的客戶端側。(有一個實驗性的過濾器: WebClientWriteResponseFilter可實現相同功能,但無需Netty)

6 RouteToRequestUrl Filter

如果exchange中的 ServerWebExchangeUtils.GATEWAY_ROUTE_ATTR 屬性中有一個 Route 對象,則運行 RouteToRequestUrlFilter 。它根據請求URI創建一個新URI,但會使用該 Route 對象的URI屬性進行更新。新URI放到exchange的 ServerWebExchangeUtils.GATEWAY_REQUEST_URL_ATTR 屬性中。

如果URI具有scheme前綴,例如 lb:ws://serviceid ,該 lb scheme將從URI中剝離,並放到 ServerWebExchangeUtils.GATEWAY_SCHEME_PREFIX_ATTR 中,方便後面的過濾器使用。

7 Websocket Routing Filter

如果exchange中的 ServerWebExchangeUtils.GATEWAY_REQUEST_URL_ATTR 屬性的值的scheme是 ws 或者 wss ,則運行Websocket Routing Filter。它底層使用Spring Web Socket將Websocket請求轉發到下游。

可爲URI添加 lb 前綴實現負載均衡,例如 lb:ws://serviceid 。

如果你使用 SockJS [1] 所謂普通http的後備,則應配置正常的HTTP路由以及Websocket路由。

spring: cloud: gateway: routes: # SockJS route - id: websocket_sockjs_route uri: http://localhost:3001 predicates: - Path=/websocket/info/ # Normwal Websocket route - id: websocket_route uri: ws://localhost:3001 predicates: - Path=/websocket/

8 Gateway Metrics Filter

要啓用Gateway Metrics,需添加 spring-boot-starter-actuator 依賴。然後,只要 spring.cloud.gateway.metrics.enabled 的值不是false,就會運行Gateway Metrics Filter。此過濾器添加名爲 gateway.requests 的時序度量(timer metric),其中包含以下標記:

• routeId :路由ID • routeUri :API將路由到的URI • outcome :由 HttpStatus.Series [2] 分類 • status :返回給客戶端的Http Status • httpStatusCode :返回給客戶端的請求的Http Status • httpMethod :請求所使用的Http方法

這些指標暴露在 /actuator/metrics/gateway.requests 端點中,並且可以輕鬆與Prometheus整合,從而創建一個 Grafana [3] dashboard [4] 。

TIPS

Prometheus是一款監控工具,Grafana是一款監控可視化工具;Spring Boot Actuator可與這兩款工具進行整合。關於整合,筆者寫過手把手的博客,有興趣可以看一下:

Spring Boot 2.x監控數據可視化(Actuator + Prometheus + Grafana手把手) [5]

9 Marking An Exchange As Routed

在網關路由 ServerWebExchange 後,它將通過在exchange添加一個 gatewayAlreadyRouted 屬性,從而將exchange標記爲 routed 。一旦請求被標記爲 routed ,其他路由過濾器將不會再次路由請求,而是直接跳過。您可以使用便捷方法將exchange標記爲 routed ,或檢查exchange是否是 routed 。

• ServerWebExchangeUtils.isAlreadyRouted 檢查是否已被路由 • ServerWebExchangeUtils.setAlreadyRouted 設置routed狀態

TIPS

簡單來說,就是網關通過 gatewayAlreadyRouted 屬性表示這個請求已經轉發過了,而無需其他過濾器重複路由。從而防止重複的路由操作。

需要java學習路線圖的私信筆者“java”領取哦!另外喜歡這篇文章的可以給筆者點個贊,關注一下,每天都會分享Java相關文章!還有不定時的福利贈送,包括整理的學習資料,面試題,源碼等~~

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章