spring cloud gateway中內置predicate、filter的使用,可以參考以下链接:
自定义filter、全局filter
先说全局filter,它是应用在所有的route的,看源码就知道,一个route的filter = 自身配的filter + defaultFilter + globalFilter。另外提一句,globalFilter往往都实现Orderer的,需要它个顺序,数值越小优先级越高,在后面filterHandlerMapping的时候,这些globalFilter会按顺序排好去执行的。
public interface GlobalFilter {
/**
* Process the Web request and (optionally) delegate to the next {@code WebFilter}
* through the given {@link GatewayFilterChain}.
* @param exchange the current server exchange
* @param chain provides a way to delegate to the next filter
* @return {@code Mono<Void>} to indicate when request processing is complete
*/
Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain);
}
GlobalFilter是一个接口,其实spring cloud gateway 里头已经有些定义好了的globalFilter,比如 NettyRoutingFilter、NettyWriteResponseFilter、ReactiveLoadBalancerClientFilter、NettyRoutingFilter & NettyWriteResponseFilter等等。


拿个ReactiveLoadBalancerClientFilter举例,ReactiveLoadBalancerClientFilter会根据 lb:// 前缀过滤处理,做负载均衡,选择最终要调用的服务地址,核心代码:
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
// 获取请求url和前缀
URI url = exchange.getAttribute(GATEWAY_REQUEST_URL_ATTR);
// 获取url scheme前缀
String schemePrefix = exchange.getAttribute(GATEWAY_SCHEME_PREFIX_ATTR);
// 1.如果请求url为空,或者不是lb开头,那直接跳过处理
if (url == null
|| (!"lb".equals(url.getScheme()) && !"lb".equals(schemePrefix))) {
return chain.filter(exchange);
}
// 2.添加原始请求url到exchange里的GATEWAY_ORIGINAL_REQUEST_URL_ATTR(LinkedHashSet)
addOriginalRequestUrl(exchange, url);
return choose(exchange).doOnNext(response -> {
// 3.获取负载均衡器,一般这里就是RibbonLoadBalancerClient
ServiceInstance retrievedInstance = response.getServer();
URI uri = exchange.getRequest().getURI();
// 判断获取到的负载均衡器是用http还是https
String overrideScheme = retrievedInstance.isSecure() ? "https" : "http";
// 假设请求url的scheme前缀不为空,这里的逻辑应该是有问题的
// 如果schemePrefix不是lb,那早在第一步就结束了
if (schemePrefix != null) {
// 那么委托的负载均衡器请求也使用同样的scheme:lb
overrideScheme = url.getScheme();
}
// 构造一个委托的负载均衡器
DelegatingServiceInstance serviceInstance = new DelegatingServiceInstance(
retrievedInstance, overrideScheme);
// 由负载均衡器选出从多个提供服务的url里选出一个
URI requestUrl = reconstructURI(serviceInstance, uri);
// 然后把这个选出的url塞到ServerWebExchange里给其他过滤器用
exchange.getAttributes().put(GATEWAY_REQUEST_URL_ATTR, requestUrl);
}).then(chain.filter(exchange));
}
自定义globalFilter
回归正题,我们可以自定义globalFilter,只需要实现globalFilter接口和ordered接口就行了。
public class CustomGlobalFilter implements GlobalFilter, Ordered {
public static final Integer CUSTOM_GLOBAL_FILTER_ORDER = 0;
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
System.out.println("let us see what is ServerWebExchange : " + exchange);
//直接下一个
//return chain.filter(exchange);
long startTime = System.currentTimeMillis();
return chain.filter(exchange).then().then(Mono.fromRunnable(() -> {
//调用请求之后统计时间
long endTime = System.currentTimeMillis();
System.out.println(exchange.getRequest().getURI().getRawPath() + ", cost time : " + (endTime - startTime) + "ms");
}));
}
@Override
public int getOrder() {
return CUSTOM_GLOBAL_FILTER_ORDER;
}

自定义局部filter
自定义局部filter,只需要实现gatewayFilter,同时往往也实现ordered,最后是实现构造工厂,如SetResponseHeaderGatewayFilterFactory,仿造就可以了,注意命名格式,因为需要在配置文件用,FilterDefination的提取问题。
public class CustomGatewayFilter implements GatewayFilter, Ordered {
private AbstractNameValueGatewayFilterFactory.NameValueConfig config;
public CustomGatewayFilter(AbstractNameValueGatewayFilterFactory.NameValueConfig config) {
this.config = config;
}
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
System.out.println("<-------------- BEGIN in custom gateway filter -------------->");
System.out.println("do something before");
return chain.filter(exchange).then(Mono.fromRunnable(() -> {
System.out.println("do something after");
System.out.println("custom gateway filter : " + toString());
System.out.println("<-------------- END in custom gateway filter -------------->");
}));
}
@Override
public int getOrder() {
return 123;
}
@Override
public String toString() {
return filterToStringCreator(this)
.append(config.getName(), config.getValue()).toString();
}
}
@Component //千万注意名字格式,XXX+GatewayFilterFactory
public class CustomGatewayFilterFactory extends AbstractNameValueGatewayFilterFactory{
@Override
public GatewayFilter apply(NameValueConfig config) {
return new CustomGatewayFilter(config);
}
}
最后在配置文件配置,filters那里,截取部分如下:
routes: #配置路由
- id: consumer_low
uri: http://192.168.1.106:8000
predicates:
- Path=/cs/** #路径
- Weight=group1,2 #权重
- After=2021-01-20T17:42:47.789+08:00[Asia/Shanghai] #在这时间后生效
- Before=2022-01-20T17:42:47.789+08:00[Asia/Shanghai] #在这时间前生效
- Between=2021-01-20T17:42:47.789+08:00[Asia/Shanghai],2022-01-20T17:42:47.789+08:00[Asia/Shanghai] #在这时间区间生效
- Method=GET #请求方式
- Cookie=orion, li.ly1 #cookies,key-value格式,只有key是包含这个参数,如果还有value就是key的值要满足value正则,http://192.168.1.106:9001/cs/gatewayPredicate/testCookie,带上cookie,注意domain,path=/,过期时间
- Query=marit, lar.sen # 请求参数,key-value格式,只有key是包含这个参数,如果还有value就是key的值要满足value正则,http://192.168.1.106:9001/cs/gatewayPredicate/testParam?orion=likly
- Header=mitch, lu.cker #请求头,key-value格式,只有key是包含这个参数,如果还有value就是key的值要满足value正则,http://192.168.1.106:9001/cs/gatewayPredicate/testHeader,Header加mitch=lugcker
filters: # 网关过滤器
- StripPrefix=1
- Custom=wawa,lala
debug查看过滤器链:

再看打印日志,可见其实是按filter chain来执行的:
<-------------- BEGIN in custom global filter -------------->
let us see what is ServerWebExchange : org.springframework.web.server.adapter.DefaultServerWebExchange@4c2e5532
<-------------- BEGIN in custom gateway filter -------------->
do something before
do something after
custom gateway filter : [CustomGatewayFilter wawa = 'lala']
<-------------- END in custom gateway filter -------------->
/cs/gatewayPredicate/testAll, cost time : 3850ms
<-------------- END in custom global filter -------------->
网友评论