引言
hello,大家好,这里是Anyin。
在上篇Spring Cloud Gateway和Spring WebFlux的契合点[1] 我们介绍了Spring Cloud Gateway是如何与Spring WebFlux 进行对接的,并且也找到了Spring Cloud Gateway的Filter的工作流程入口FilteringWebHandler#handle。
我们知道,在Spring Cloud Gateway的Filter区分2个类型:GlobalFilter和GatewayFilter,GlobalFilter类型的过滤器是单例,全局唯一,所有的路由都会应用,而GatewayFilter并非单例,是在路由加载的时候挂载到具体某个路由对象上的。
今天,我们就来介绍下Spring Cloud Gateway 过滤器的具体工作流程
过滤器合并
在上一节,我们知道Spring Cloud Gateway有2个类型的过滤器,那它是如何应用到过滤器的呢? 这里我们来看下FilteringWebHandler#loadFilters的方法。
private static List<GatewayFilter> loadFilters(List<GlobalFilter> filters) {
return filters.stream().map(filter -> {
GatewayFilterAdapter gatewayFilter = new GatewayFilterAdapter(filter);
if (filter instanceof Ordered) {
int order = ((Ordered) filter).getOrder();
return new OrderedGatewayFilter(gatewayFilter, order);
}
return gatewayFilter;
}).collect(Collectors.toList());
}
在这里我们可以看到,通过GatewayFilterAdapter类代理了GlobalFilter,把它包装成GatewayFilter。这里就是一个代理模式的应用:GatewayFilterAdapter实现了GatewayFilter的接口,并且持有一个GlobalFilter的引用,通过这个代理类,成功把GlobalFilter包装成GatewayFilter。
接着在FilteringWebHandler#handle方法内,把已经挂载在路由对象上的GatewayFilter列表和GlobalFilter列表合并。
public Mono<Void> handle(ServerWebExchange exchange) {
Route route = exchange.getRequiredAttribute(GATEWAY_ROUTE_ATTR);
List<GatewayFilter> gatewayFilters = route.getFilters();
// 两个列表进行合并并排序
List<GatewayFilter> combined = new ArrayList<>(this.globalFilters);
combined.addAll(gatewayFilters);
AnnotationAwareOrderComparator.sort(combined);
if (logger.isDebugEnabled()) {
logger.debug("Sorted gatewayFilterFactories: " + combined);
}
// 通过责任链模式对请求进行处理
return new DefaultGatewayFilterChain(combined).filter(exchange);
}
过滤器具体工作流程
在上一节,我们可以看到所有的过滤器对象都放入了DefaultGatewayFilterChain类,然后执行该对象的filter方法。我们接着看下DefaultGatewayFilterChain类的内部实现。
DefaultGatewayFilterChain类其实是一个责任链模式的编排器,它持有2个成员变量:
•List<GatewayFilter> filters 所有需要执行的过滤器列表•int index 当前需要执行的过滤器索引
它有2个构造方法:
•DefaultGatewayFilterChain(List<GatewayFilter> filters) 第一次初始化的时候需要,把index置为0,放入过滤器列表•DefaultGatewayFilterChain(DefaultGatewayFilterChain parent, int index) 每次过滤器执行之前,需要把下一次执行的过滤器的索引和本次的执行的信息放入到下一个过滤器链中
在FilteringWebHandler.DefaultGatewayFilterChain#filter方法中,执行了过滤器链,代码如下:
public Mono<Void> filter(ServerWebExchange exchange) {
return Mono.defer(() -> {
if (this.index < filters.size()) {
GatewayFilter filter = filters.get(this.index);
DefaultGatewayFilterChain chain = new DefaultGatewayFilterChain(this, this.index + 1);
return filter.filter(exchange, chain);
}
else {
return Mono.empty(); // complete
}
});
}
在每次执行过滤器的filter方法之前,都会重新创建一个DefaultGatewayFilterChain对象,并且把下一次需要执行的过滤器索引和过滤器列表放入到DefaultGatewayFilterChain中,直到最后过滤器列表全部执行完成,则退出循环。
过滤器
在Spring Cloud Gateway 有些比较重要的过滤器,我们在这里也稍微看下。
• NettyRoutingFilter 路由过滤器,把具体请求转发到下游服务,这里是通过reactor.netty.http.client.HttpClient包进行转发的
• ReactiveLoadBalancerClientFilter 负载均衡过滤器,该过滤器主要是针对结合了注册中心,使用lb://开头的代理路由,通过配置的代理路由,解析出具体的服务名称,接着结合spring-cloud-starter-loadbalancer组件,根据服务名找到对应的实例,然后进行客户端负载均衡,最终返回具体实例的转发地址,然后进行转发。
以下是结合注册中心的Spring Cloud Gateway 路由配置示例:
gateway:
routes:
- id: anyin-center-base
uri: lb://anyin-center-base
predicates:
- Path=/base/**
filters:
- StripPrefix=1
• uri: lb://anyin-center-base 指明了代理路由信息,lb开头表名是一个需要客户端负载均衡的路由,其服务名是anyin-center-base
• predicates:Path 指明了代理路径,即访问 http://xxx/base 是访问到base服务的
• filters.StripPrefix 指明需要忽略前缀的个数,这里是表示忽略base这个前缀即可以访问base服务的根路径
• WebsocketRoutingFilter 正常的网关转发都是http协议,而Spring Cloud Gateway可以支持Websocket协议的转发,就通过该过滤器实现。
最后
通过3篇关于Spring Cloud Gateway源码的分析,相信你已经掌握了基本的Spring Cloud Gateway用法了。
汇总其他2篇Spring Cloud Gateway的链接
• 实现Spring Cloud Gateway 动态路由和内置过滤器[2]
• Spring Cloud Gateway和Spring WebFlux的契合点[3]
以上,如果有哪里不对,欢迎讨论。
References
[1] Spring Cloud Gateway和Spring WebFlux的契合点: https://juejin.cn/post/7041055771768913927
[2] 实现Spring Cloud Gateway 动态路由和内置过滤器: https://juejin.cn/post/7038231474465669157
[3] Spring Cloud Gateway和Spring WebFlux的契合点: https://juejin.cn/post/7041055771768913927