引言
hello,大家好,这里是Anyin。
在上篇# Spring Cloud Gateway和Spring WebFlux的契合点 我们介绍了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的链接
以上,如果有哪里不对,欢迎讨论。
作者:Anyin
链接:Spring Cloud Gateway 过滤器工作流程 - 掘金