要說到gateway閘道器過濾,那一定要明白-過濾器-自定義區域性、全域性過濾器、區別。
前兩天除錯gateway閘道器問題,發現GatewayFilter和GlobalFilter使用錯了,導致導致閘道器攔截失效,搞了一上午才找到問題。
自定義過濾器需要實現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 filter(ServerWebExchange exchange, GatewayFilterChain chain),唯一的區別就是GatewayFilter繼承了ShortcutConfigurable,GlobalFilter沒有任何繼承。
參看示例一:Gateway Filter與RouteLocator繫結使用
3.2、GlobalFilter
Spring Cloud gateway定義了GlobalFilter的介面讓我們去自定義實現自己的的GlobalFilter。GlobalFilter是一個全域性的Filter,作用於所有的路由。
讓其在Gateway中執行生效,有兩種方式一種直接加[@Component](https://github.com/Component "@Component")
註解,另外一種可以在 Spring Config 中配置這個 Bean如下示例二所示;
本作品採用《CC 協議》,轉載必須註明作者和本文連結