參考:https://cloud.spring.io/spring-cloud-static/Greenwich.SR2/single/spring-cloud.html#_global_filtershtml
全局過濾器 | 做用 |
---|---|
Combined Global Filter and GatewayFilter Ordering | 對過濾器執行順序進行排序 |
Forward Routing Filter | 用於本地forward,也就是將請求在Gateway服務內進行轉發,而不是轉發到下游服務 |
LoadBalancerClient Filter | 整合Ribbon實現負載均衡 |
Netty Routing Filter | 使用Netty的 HttpClient 轉發http、https請求 |
Netty Write Response Filter | 將代理響應寫回網關的客戶端側 |
RouteToRequestUrl Filter | 將從request裏獲取的原始url轉換成Gateway進行請求轉發時所使用的url |
Websocket Routing Filter | 使用Spring Web Socket將轉發 Websocket 請求 |
Gateway Metrics Filter | 整合監控相關,提供監控指標 |
Marking An Exchange As Routed | 防止重複的路由轉發 |
當Gateway接收到請求時,Filtering Web Handler 處理器會將全部的 GlobalFilter 實例以及全部路由上所配置的 GatewayFilter 實例添加到一條過濾器鏈中。該過濾器鏈裏的全部過濾器都會按照 org.springframework.core.Ordered 註解所指定的數字大小進行排序。
Spring Cloud Gateway區分了過濾器邏輯執行的 」pre」 和 」post」 階段,因此優先級高的過濾器將會在 「pre」 階段最早執行,優先級最低的過濾器則在 「post」 階段最後執行。web
- 數字越小越靠前執行
示例代碼:spring
@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"); })); }; }
返回結果:websocket
first pre filter second pre filter third pre filter first post filter second post filter third post filter
當請求進來時,ForwardRoutingFilter 會查看一個URL,該URL爲 exchange 屬性 ServerWebExchangeUtils.GATEWAY_REQUEST_URL_ATTR 的值,若是該 url 的 scheme 是 forward(例如:forward://localendpoint),那麼該Filter會使用Spirngd的DispatcherHandler 來處理這個請求。該請求的URL路徑部分,會被forward URL中的路徑覆蓋掉。而未修改過的原始URL,會被追加到 ServerWebExchangeUtils.GATEWAY_ORIGINAL_REQUEST_URL_ATTR 屬性中。app
PS:所謂 url scheme 簡單來講就是 url 中的協議部分,例如http、https、ws等。自定義的 scheme 一般用於標識該url的行爲,例如app開發中一般使用url scheme來跳轉頁面負載均衡
這個Filter是用來整合Ribbon的,其核心就是解析 scheme 爲lb的 url,以此獲取微服務的名稱,而後再經過Ribbon獲取實際的調用地址。socket
當請求進來時,LoadBalancerClientFilter 會查看一個URL,該URL爲 exchange 的屬性 ServerWebExchangeUtils.GATEWAY_REQUEST_URL_ATTR 的值,若是該 url 的 scheme 是 lb,(例如:lb://myservice ),那麼該Filter會使用Spring Cloud的 LoadBalancerClient 來將 myservice 解析成實際的host 和 port ,並替換掉本來 ServerWebExchangeUtils.GATEWAY_REQUEST_URL_ATTR 屬性的值。而原始 url 會追加到 ServerWebExchangeUtils.GATEWAY_ORIGINAL_REQUEST_URL_ATTR 屬性中。該過濾器還會查看 ServerWebExchangeUtils.GATEWAY_SCHEME_PREFIX_ATTR 屬性,若是發現該屬性的值是 lb ,也會執行相同邏輯。spring-boot
示例配置:微服務
spring: cloud: gateway: routes: - id: myRoute uri: lb://service predicates: - Path=/service/**
默認狀況下,若是沒法經過 LoadBalancer 找到指定服務的實例,那麼會返回503(如上配置示例, 若 LoadBalancer 找不到名爲 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 的配置。工具
當請求進來時,NettyRoutingFilter 會查看一個URL,該URL是 exchange 的屬性 ServerWebExchangeUtils.GATEWAY_REQUEST_URL_ATTR 的值,若是該 url 的 scheme 是 http 或 https ,那麼該Filter會使用 Netty 的 HttpClient 向下遊的服務發送代理請求。得到的響應將放在 exchange 的 ServerWebExchangeUtils.CLIENT_RESPONSE_ATTR 屬性中,以便在後面的 Filter 裏使用。(有一個實驗性的過濾器: WebClientHttpRoutingFilter 可實現相同功能,但無需Netty)
NettyWriteResponseFilter 用於將代理響應寫回網關的客戶端側,因此該過濾器會在全部其餘過濾器執行完成後才執行,而且執行的條件是 exchange 中 ServerWebExchangeUtils.CLIENT_RESPONSE_CONN_ATTR 屬性的值不爲空,該值爲 Netty 的 Connection 實例。(有一個實驗性的過濾器: WebClientWriteResponseFilter 可實現相同功能,但無需Netty)
這個過濾器用於將從request裏獲取的原始url轉換成Gateway進行請求轉發時所使用的url。當請求進來時,RouteToRequestUrlFilter 會從 exchange 中獲取 ServerWebExchangeUtils.GATEWAY_ROUTE_ATTR 屬性的值,該值是一個 Route 對象。若該對象不爲空的話,RouteToRequestUrlFilter 會基於請求 URL 及 Route 對象裏的 URL 來建立一個新的 URL。新 URL 會被放到 exchange 的 ServerWebExchangeUtils.GATEWAY_REQUEST_URL_ATTR 屬性中。
若是 URL 具備 scheme 前綴,例如 lb:ws://serviceid ,該 lb scheme將從URL中剝離,並放到 ServerWebExchangeUtils.GATEWAY_SCHEME_PREFIX_ATTR 中,方便後面的過濾器使用。
該過濾器的做用與 NettyRoutingFilter 相似。當請求進來時,WebsocketRoutingFilter 會查看一個URL,該URL是 exchange 中 ServerWebExchangeUtils.GATEWAY_REQUEST_URL_ATTR 屬性的值,若是該 url 的 scheme 是 ws 或者 wss,那麼該Filter會使用 Spring Web Socket 將 Websocket 請求轉發到下游。
另外,若是 Websocket 請求須要負載均衡的話,可爲URL添加 lb 前綴以實現負載均衡,例如 lb:ws://serviceid 。
示例配置:
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/**
想要啓用Gateway Metrics Filter,需在項目中添加 spring-boot-starter-actuator 依賴,而後在配置文件中配置 spring.cloud.gateway.metrics.enabled 的值爲true。該過濾器會添加名爲 gateway.requests 的時序度量(timer metric),其中包含如下標記:
- routeId:路由ID
- routeUri:API將路由到的URI
- outcome:由 HttpStatus.Series 分類
- status:返回給客戶端的Http Status
- httpStatusCode:返回給客戶端的請求的Http Status
- httpMethod:請求所使用的Http方法
這些指標暴露在 /actuator/metrics/gateway.requests 端點中,而且能夠輕鬆與 Prometheus 整合,從而建立一個 Grafana dashboard。
PS:Prometheus是一款監控工具,Grafana是一款監控可視化工具;Spring Boot Actuator可與這兩款工具進行整合。
當一個請求走完整條過濾器鏈後,負責轉發請求到下游的那個過濾器會在 exchange 中添加一個 gatewayAlreadyRouted 屬性,從而將 exchange 標記爲 routed(已路由)。一旦請求被標記爲 routed ,其餘路由過濾器將不會再次路由該請求,而是直接跳過。
瞭解了以上全部內置的全局過濾器後,咱們知道不一樣協議的請求會由不一樣的過濾器轉發到下游。因此負責添加這個gatewayAlreadyRouted 屬性的過濾器就是最終負責轉發請求的過濾器:
- http、https請求會由NettyRoutingFilter或WebClientHttpRoutingFilter添加這個屬性
- forward請求會由ForwardRoutingFilter添加這個屬性
- websocket請求會由WebsocketRoutingFilter添加這個屬性
這些過濾器調用瞭如下方法將 exchange 標記爲 routed ,或檢查 exchange 是不是 routed:- ServerWebExchangeUtils.isAlreadyRouted:檢查exchange是否爲routed狀態
- ServerWebExchangeUtils.setAlreadyRouted:將exchange設置爲routed狀態
簡單來講,就是Gateway經過 gatewayAlreadyRouted 屬性表示這個請求已經轉發過了,而無需其餘過濾器重複路由,從而防止重複的路由轉發。
這些全局過濾器都有對應的配置類,感興趣的話能夠查看相關源碼:
- org.springframework.cloud.gateway.config.GatewayAutoConfiguration
- org.springframework.cloud.gateway.config.GatewayMetricsAutoConfiguration
- org.springframework.cloud.gateway.config.GatewayLoadBalancerClientAutoConfiguration