一、自定義局部過濾器
自定義過濾器需要實現GatewayFilter
和Ordered
。其中GatewayFilter
中的這個方法就是用來實現你的自定義的邏輯的
Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain);
示例:統計某個服務的響應時間
1.1、創建Filer
public class ElapsedFilter implements GatewayFilter, Ordered { private static final Log log = LogFactory.getLog(GatewayFilter.class); private static final String ELAPSED_TIME_BEGIN = "elapsedTimeBegin"; @Override public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) { exchange.getAttributes().put(ELAPSED_TIME_BEGIN, System.currentTimeMillis()); return chain.filter(exchange).then( Mono.fromRunnable(() -> { Long startTime = exchange.getAttribute(ELAPSED_TIME_BEGIN); if (startTime != null) { log.info(exchange.getRequest().getURI().getRawPath() + ": " + (System.currentTimeMillis() - startTime) + "ms"); } }) ); } @Override public int getOrder() { return Ordered.LOWEST_PRECEDENCE; } }
在請求剛剛到達時,往ServerWebExchange
中放入了一個屬性elapsedTimeBegin
,屬性值為當時的毫秒級時間戳。然后在請求執行結束后,又從中取出我們之前放進去的那個時間戳,與當前時間的差值即為該請求的耗時。因為這是與業務無關的日志所以將Ordered
設為Integer.MAX_VALUE
以降低優先級。
chain.filter(exchange)
之前的就是 “pre” 部分,之后的也就是then
里邊的是 “post” 部分。
1.2、配置
創建好 Filter 之后我們將它添加到我們的 Filter Chain 里邊
@Bean public RouteLocator customerRouteLocator(RouteLocatorBuilder builder) { // @formatter:off return builder.routes() .route(r -> r.path("/fluent/customer/**") .filters(f -> f.stripPrefix(2) .filter(new ElapsedFilter()) .addResponseHeader("X-Response-Default-Foo", "Default-Bar")) .uri("lb://CONSUMER") .order(0) .id("fluent_customer_service") ) .build(); // @formatter:on }
注意:實際在使用 Spring Cloud 的過程中,需要使用 Sleuth+Zipkin 來進行耗時分析。
二、全局過濾器
有多個路由就需要一個一個來配置,並不能通過像下面這樣來實現全局有效(也未在 Fluent Java API 中找到能設置 defaultFilters 的方法)
@Bean public ElapsedFilter elapsedFilter(){ return new ElapsedFilter(); }
自定義過濾器需要實現GlobalFilter和Ordered
。
示例:校驗token
2.1、創建Filter
public class TokenFilter implements GlobalFilter, Ordered { @Override public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) { String token = exchange.getRequest().getQueryParams().getFirst("token"); if (token == null || token.isEmpty()) { exchange.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED); return exchange.getResponse().setComplete(); } return chain.filter(exchange); } @Override public int getOrder() { return -100; } }
2.2、在 Spring Config 中配置這個 Bean
@Bean public TokenFilter tokenFilter(){ return new TokenFilter(); }
三、GatewayFilter與GlobalFilter的區別
3.1、GatewayFilter
在一個高的角度來看,Global filters會被應用到所有的路由上,而Gateway filter將應用到單個路由
上或者一個分組的路由
上。
GatewayFilter是從WebFilter中Copy過來的,相當於一個Filter過濾器,可以對訪問的URL過濾橫切處理,應用場景比如超時,安全等。
GatewayFilter和GlobalFilter兩個接口中定義的方法一樣都是Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain),唯一的區別就是GatewayFilter繼承了ShortcutConfigurable,GlobalFilter沒有任何繼承。
參看示例一:Gateway Filter與RouteLocator綁定使用
3.2、GlobalFilter
Spring Cloud gateway定義了GlobalFilter的接口讓我們去自定義實現自己的的GlobalFilter。GlobalFilter是一個全局的Filter,作用於所有的路由。
讓其在Gateway中運行生效,有兩種方式一種直接加@Component
注解,另外一種可以在 Spring Config 中配置這個 Bean如下示例二所示;