【轉載】Spring Cloud Gateway-全域性過濾器(Global Filters)
http://www.imooc.com/article/290821
TIPS
本文基於Spring Cloud Gateway SR2,理論適配Spring Cloud Gateway SR1以及更高版本。
本文詳細探討Spring Cloud Gateway內建的全域性過濾器。包括:
1 Combined Global Filter and GatewayFilter Ordering
2 Forward Routing Filter
3 LoadBalancerClient Filter
4 Netty Routing Filter
5 Netty Write Response Filter
7 Websocket Routing Filter
8 Gateway Metrics Filter
9 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
這段文件太學術了,講解了
ForwardRoutingFilter
的實現原理,對使用者來說,意義不大;對使用者來說,只要知道這個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 。它使用NettyHttpClient
向下遊傳送代理請求。獲得的響應將放在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所謂普通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
:路由IDrouteUri
:API將路由到的URIoutcome
:由HttpStatus.Series分類status
:返回給客戶端的Http StatushttpStatusCode
:返回給客戶端的請求的Http StatushttpMethod
:請求所使用的Http方法
這些指標暴露在/actuator/metrics/gateway.requests
端點中,並且可以輕鬆與Prometheus整合,從而建立一個Grafanadashboard。
TIPS
Prometheus是一款監控工具,Grafana是一款監控視覺化工具;Spring Boot Actuator可與這兩款工具進行整合。關於整合,筆者寫過手把手的部落格,有興趣可以看一下:
9 Marking An Exchange As Routed
在閘道器路由ServerWebExchange
後,它將通過在exchange新增一個gatewayAlreadyRouted
屬性,從而將exchange標記為routed
。一旦請求被標記為routed
,其他路由過濾器將不會再次路由請求,而是直接跳過。您可以使用便捷方法將exchange標記為routed
,或檢查exchange是否是routed
。
ServerWebExchangeUtils.isAlreadyRouted
檢查是否已被路由ServerWebExchangeUtils.setAlreadyRouted
設定routed狀態
TIPS
簡單來說,就是閘道器通過
gatewayAlreadyRouted
屬性表示這個請求已經轉發過了,而無需其他過濾器重複路由。從而防止重複的路由操作。