1. 程式人生 > 實用技巧 >【轉載】Spring Cloud Gateway-全域性過濾器(Global Filters)

【轉載】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

6 RouteToRequestUrl 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返回的ServiceInstanceisSecure的值,會覆蓋請求的scheme。舉個例子,如果請求打到Gateway上使用的是HTTPS,但ServiceInstanceisSecure是false,那麼下游收到的則是HTTP請求,反之亦然。然而,如果該路由指定了GATEWAY_SCHEME_PREFIX_ATTR屬性,那麼字首將會被剝離,並且路由URL中的scheme會覆蓋ServiceInstance的配置

TIPS

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

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

4 Netty Routing Filter

如果ServerWebExchangeUtils.GATEWAY_REQUEST_URL_ATTR的值的scheme是httphttps,則執行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,該lbscheme將從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:路由ID
  • routeUri:API將路由到的URI
  • outcome:由HttpStatus.Series分類
  • status:返回給客戶端的Http Status
  • httpStatusCode:返回給客戶端的請求的Http Status
  • httpMethod:請求所使用的Http方法

這些指標暴露在/actuator/metrics/gateway.requests端點中,並且可以輕鬆與Prometheus整合,從而建立一個Grafanadashboard

TIPS

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

Spring Boot 2.x監控資料視覺化(Actuator + Prometheus + Grafana手把手)

9 Marking An Exchange As Routed

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

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

TIPS

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

參考文件

本文首發

http://www.itmuch.com/spring-cloud-gateway/global-filter/