Global Filters
GlobalFilter
接口方法和GatewayFilter
是一样的,GlobalFilter
特别之处在于它的作用是全局的。
1. Combined Global Filter and GatewayFilter Ordering
当请求到来时,Filtering Web Handler
处理器会添加所有GlobalFilter
实例和匹配的GatewayFilter
实例到过滤器链中,通过对filter
bean配置注解@Order
,则过滤器链会对这些过滤器实例bean
进行排序。
Spring Cloud Gateway将过滤器的逻辑按请求执行点分为”pre”和”post”的一前一后处理,如果是高优先级的过滤器,则在”pre”逻辑中最先执行,在”post”逻辑中最后执行。
ExampleConfiguration.java.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32
|
@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"); })); }; }
|
上面例子陆续会打印的是:
1 2 3 4 5 6
|
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内容,如果url的scheme是forward
,比如:forward://localendpoint
,则它会使用Spirng的DispatcherHandler
来处理这个请求。
源码实现:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
|
@Override public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) { URI requestUrl = exchange.getRequiredAttribute(GATEWAY_REQUEST_URL_ATTR);
String scheme = requestUrl.getScheme(); if (isAlreadyRouted(exchange) || !"forward".equals(scheme)) { return chain.filter(exchange); } setAlreadyRouted(exchange);
|
3. LoadBalancerClient Filter
LoadBalancerClientFilter
会查看exchange的属性ServerWebExchangeUtils.GATEWAY_REQUEST_URL_ATTR
的URI内容,如果url的scheme是lb
,比如:lb://myservice
,或者是ServerWebExchangeUtils.GATEWAY_SCHEME_PREFIX_ATTR
属性的内容是lb
,则它会使用Spring Cloud的LoadBalancerClient
来将host转化为实际的host和port,并以此替换属性ServerWebExchangeUtils.GATEWAY_REQUEST_URL_ATTR
的内容,原来的URL则会被添加到ServerWebExchangeUtils.GATEWAY_ORIGINAL_REQUEST_URL_ATTR
属性的列表中。
源码实现:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33
|
@Override public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) { URI url = exchange.getAttribute(GATEWAY_REQUEST_URL_ATTR); String schemePrefix = exchange.getAttribute(GATEWAY_SCHEME_PREFIX_ATTR); if (url == null || (!"lb".equals(url.getScheme()) && !"lb".equals(schemePrefix))) { return chain.filter(exchange); }
|
application.yml.
1 2 3 4 5 6 7 8
|
spring: cloud: gateway: routes: - id: myRoute uri: lb://service predicates: - Path=/service/**
|
默认情况下,如果LoadBalancer
找不到服务实例,则会返回HTTP状态码503
,你也可以通过修改spring.cloud.gateway.loadbalancer.use404=true
配置修改为返回状态码404
。
4. Netty Routing Filter
如果ServerWebExchangeUtils.GATEWAY_REQUEST_URL_ATTR
属性中的url的scheme是http
或https
,则Netty Routing Filter才会执行,并使用Netty作为http请求客户端对下游进行代理请求。请求的响应会放在exchange的ServerWebExchangeUtils.CLIENT_RESPONSE_ATTR
属性中,以便后面的filter做进一步的处理。
5. Netty Write Response Filter
如果NettyWriteResponseFilter
发现exchange的ServerWebExchangeUtils.CLIENT_RESPONSE_ATTR
属性中存在Netty的HttpClientResponse
类型实例,在所有过滤器都执行完毕后,它会将响应写回到gateway客户端的响应中。
源码实现:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32
|
@Override public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
|
6. RouteToRequestUrl Filter
如果exchange的ServerWebExchangeUtils.GATEWAY_ROUTE_ATTR
属性存放了Route
对象,则RouteToRequestUrlFilter
会根据基于请求的URI创建新的URI,新的URI会更新到ServerWebExchangeUtils.GATEWAY_REQUEST_URL_ATTR
属性中。
如果URI有scheme前缀,比如:lb:ws://serviceid
,lb
scheme截取出来放到ServerWebExchangeUtils.GATEWAY_SCHEME_PREFIX_ATTR
属性中,方便后面的filter使用。
源码实现:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
|
@Override public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) { Route route = exchange.getAttribute(GATEWAY_ROUTE_ATTR); if (route == null) { return chain.filter(exchange); } log.trace("RouteToRequestUrlFilter start"); URI uri = exchange.getRequest().getURI(); boolean encoded = containsEncodedParts(uri); URI routeUri = route.getUri();
if (hasAnotherScheme(routeUri)) {
|
7. Websocket Routing Filter
如果请求URL的scheme是ws
或wss
的话,那么Websocket Routing Filter就会使用Spring Web Socket底层来处理对下游的请求转发。
如果Websocket也使用了负载均衡,则需要这样配置:lb:ws://serviceid
.
源码实现:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
|
@Override public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) { changeSchemeIfIsWebSocketUpgrade(exchange);
URI requestUrl = exchange.getRequiredAttribute(GATEWAY_REQUEST_URL_ATTR); String scheme = requestUrl.getScheme();
if (isAlreadyRouted(exchange) || (!"ws".equals(scheme) && !"wss".equals(scheme))) { return chain.filter(exchange); } setAlreadyRouted(exchange);
HttpHeaders headers = exchange.getRequest().getHeaders(); HttpHeaders filtered = filterRequest(getHeadersFilters(), exchange);
List<String> protocols = headers.get(SEC_WEBSOCKET_PROTOCOL); if (protocols != null) { protocols = headers.get(SEC_WEBSOCKET_PROTOCOL).stream() .flatMap(header -> Arrays.stream(commaDelimitedListToStringArray(header))) .map(String::trim) .collect(Collectors.toList()); }
return this.webSocketService.handleRequest(exchange, new ProxyWebSocketHandler(requestUrl, this.webSocketClient, filtered, protocols)); }
|
8. Making An Exchange As Routed
上面一些像ForwardRoutingFilter、Websocket Routing Filter的源码中,都可以清楚看到gateway通过设置gatewayAlreadyRouted
标识这个请求是否已经路由转发出去了,无需其他filter重复路由,这样就可以避免重复错误的路由操作,保证了路由的实现灵活性。
ServerWebExchangeUtils.isAlreadyRouted
检查是否已被路由,ServerWebExchangeUtils.setAlreadyRouted
标记已被路由状态。
转自:https://www.edjdhbb.com/2019/01/06/spring%20cloud%20gateway%E7%B3%BB%E5%88%97%E6%95%99%E7%A8%8B3%E2%80%94Global%20Filters/