Spring Cloud Gateway-全局過濾器(Global Filters)

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 filter
second pre filter
third pre filter
first post filter
second post filter
third 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 屬性表示這個請求已經轉發過了,而無需其他過濾器重複路由。從而防止重複的路由操作。

參考文檔

•114. Global Filters[6]

乾貨分享

最近將個人學習筆記整理成冊,使用PDF分享。關注我,回覆如下代碼,即可獲得百度盤地址,無套路領取!

•001:《Java併發與高併發解決方案》學習筆記;

•002:《深入JVM內核——原理、診斷與優化》學習筆記;

•003:《Java面試寶典》

•004:《Docker開源書》

•005:《Kubernetes開源書》

•006:《DDD速成(領域驅動設計速成)》

References

[1] SockJS: https://github.com/sockjs

[2] HttpStatus.Series: https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/http/HttpStatus.Series.html

[3] Grafana: https://cloud.spring.io/spring-cloud-gateway/reference/html/images/gateway-grafana-dashboard.jpeg

[4] dashboard: https://cloud.spring.io/spring-cloud-gateway/reference/html/gateway-grafana-dashboard.json

[5] Spring Boot 2.x監控數據可視化(Actuator + Prometheus + Grafana手把手): http://www.itmuch.com/spring-boot/actuator-prometheus-grafana/

[6] 114. Global Filters: https://cloud.spring.io/spring-cloud-static/Greenwich.SR2/single/spring-cloud.html#_global_filters


分享到:


相關文章: