009-spring cloud gateway-过滤器GatewayFilter、GlobalFilter、GatewayFilterChain、作用、生命周期、GatewayFilterFactory内置过滤器
一、概述
在Spring-Cloud-Gateway之请求处理流程中最终网关是将请求交给过滤器链表进行处理。
核心接口:GatewayFilter,GlobalFilter,GatewayFilterChain。
查看整体类图
二、网关过滤器作用
当使用微服务构建整个 API 服务时,一般有许多不同的应用在运行,如上图所示的mst-user-service
、mst-good-service
和mst-order-service
,这些服务都需要对客户端的请求的进行 Authentication。最简单粗暴的方法就是像上图一样,为每个微服务应用都实现一套用于校验的过滤器或拦截器。
通过前置的网关服务来完成这些非业务性质的校验。
三、Filter 的生命周期
Spring Cloud Gateway 的 Filter 的生命周期有两个:“pre” 和 “post”。
“pre”和 “post” 分别会在请求被执行前调用和被执行后调用,和 Zuul Filter 或 Spring Interceptor 中相关生命周期类似,但在形式上有些不一样。
Zuul 的 Filter 是通过filterType()
方法来指定,一个 Filter 只能对应一种类型,要么是 “pre” 要么是“post”。Spring Interceptor 是通过重写HandlerInterceptor
中的三个方法来实现的。而 Spring Cloud Gateway 基于 Project Reactor 和 WebFlux,采用响应式编程风格,打开它的 Filter 的接口GatewayFilter
你会发现它只有一个方法filter
。
四、核心接口解读
4.1、GatewayFilterChain--网关过滤链表
/**
* 网关过滤链表接口
* 用于过滤器的链式调用
*/
public interface GatewayFilterChain { /**
* 链表启动调用入口方法*/
Mono<Void> filter(ServerWebExchange exchange); }
默认实现
/**
* 网关过滤的链表,用于过滤器的链式调用
* 过滤器链表接口的默认实现,
* 包含2个构建函数:
* 1.集合参数构建用于初始化吧构建链表
* 2. index,parent参数用于构建当前执行过滤对应的下次执行的链表
*/
private static class DefaultGatewayFilterChain implements GatewayFilterChain { /**
* 当前过滤执行过滤器在集合中索引
*/
private final int index;
/**
* 过滤器集合
*/
private final List<GatewayFilter> filters; public DefaultGatewayFilterChain(List<GatewayFilter> filters) {
this.filters = filters;
this.index = 0;
} /**
* 构建
* @param parent 上一个执行过滤器对应的FilterChain
* @param index 当前要执行过滤器的索引
*/
private DefaultGatewayFilterChain(DefaultGatewayFilterChain parent, int index) {
this.filters = parent.getFilters();
this.index = index;
} public List<GatewayFilter> getFilters() {
return filters;
} /**
* @param exchange the current server exchange
* @return
*/
@Override
public Mono<Void> filter(ServerWebExchange exchange) {
return Mono.defer(() -> {
if (this.index < filters.size()) {
//获取当前索引的过滤器
GatewayFilter filter = filters.get(this.index);
//构建当前索引的下一个过滤器的FilterChain
DefaultGatewayFilterChain chain = new DefaultGatewayFilterChain(this, this.index + 1);
//调用过滤器的filter方法执行过滤器
return filter.filter(exchange, chain);
} else {
//当前索引大于等于过滤集合大小,标识所有链表都已执行完毕,返回空
return Mono.empty(); // complete
}
});
}
}
过滤器的GatewayFilterChain 执行顺序
- 通过GatewayFilter集合构建顶层的GatewayFilterChain
- 调用顶层GatewayFilterChain,获取第一个Filter,并创建下一个Filter索引对应的GatewayFilterChain
- 调用filter的filter方法执行当前filter,并将下次要执行的filter对应GatewayFilterChain传入。
4.2、GatewayFilter--网关路由过滤器
/**
* 网关路由过滤器,
* Contract for interception-style, chained processing of Web requests that may
* be used to implement cross-cutting, application-agnostic requirements such
* as security, timeouts, and others. Specific to a Gateway
*
*/
public interface GatewayFilter extends ShortcutConfigurable { String NAME_KEY = "name";
String VALUE_KEY = "value"; /**
* 过滤器执行方法
* 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); }
网关过滤器接口,有且只有一个方法filter,执行当前过滤器,并在此方法中决定过滤器链表是否继续往下执行。
1️⃣、OrderedGatewayFilter--排序
/**
* 排序的网关路由过滤器,用于包装真实的网关过滤器,已达到过滤器可排序
*/
public class OrderedGatewayFilter implements GatewayFilter, Ordered { //目标过滤器
private final GatewayFilter delegate;
//排序字段
private final int order; public OrderedGatewayFilter(GatewayFilter delegate, int order) {
this.delegate = delegate;
this.order = order;
} @Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
return this.delegate.filter(exchange, chain);
}
}
OrderedGatewayFilter实现类主要目的是为了将目标过滤器包装成可排序的对象类型。是目标过滤器的包装类
2️⃣、GatewayFilterAdapter
/**
* 全局过滤器的包装类,将全局路由包装成统一的网关过滤器
*/
private static class GatewayFilterAdapter implements GatewayFilter { /**
* 全局过滤器
*/
private final GlobalFilter delegate; public GatewayFilterAdapter(GlobalFilter delegate) {
this.delegate = delegate;
} @Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
return this.delegate.filter(exchange, chain);
}
}
GatewayFilterAdapter实现类主要目的是为了将GlobalFilter过滤器包装成GatewayFilter类型的对应。是GlobalFilter过滤器的包装类
4.3、GlobalFilter
GlobalFilter 为请求业务以及路由的URI转换为真实业务服务的请求地址的核心过滤器,不需要配置,模式系统初始化时加载,并作用在每个路由上。
1️⃣、初始化加载,通过GatewayAutoConfiguration自动创建
//GatewayAutoConfiguration 类
/**
* 全局过滤器,用户通过HttpClient转发请求
*/
@Bean
public NettyRoutingFilter routingFilter(HttpClient httpClient,
ObjectProvider<List<HttpHeadersFilter>> headersFilters) {
return new NettyRoutingFilter(httpClient, headersFilters);
} /**
* 全局的过滤器,用户将HttpClient客户端转发请求的响应写入到原始的请求响应中
*/
@Bean
public NettyWriteResponseFilter nettyWriteResponseFilter(GatewayProperties properties) {
return new NettyWriteResponseFilter(properties.getStreamingMediaTypes());
} //GatewayLoadBalancerClientAutoConfiguration 类
/**
* 全局过滤器,用于在通过负载均衡客户端选择服务实例信息
*/
@Bean
@ConditionalOnBean(LoadBalancerClient.class)
public LoadBalancerClientFilter loadBalancerClientFilter(LoadBalancerClient client) {
return new LoadBalancerClientFilter(client);
}
2️⃣、GlobalFilter转换成GatewayFilter,并作用于每个路由上,在FilteringWebHandler实现
//FilteringWebHandler类
/**
* 包装加载全局的过滤器,将全局过滤器包装成GatewayFilter
*/
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());
}
@Override
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);
//对过滤器进行排序
//TODO: needed or cached?
AnnotationAwareOrderComparator.sort(combined); logger.debug("Sorted gatewayFilterFactories: "+ combined);
//创建过滤器链表对其进行链式调用
return new DefaultGatewayFilterChain(combined).filter(exchange);
}
- loadFilters方法是将全局路由使用GatewayFilterAdapter包装成GatewayFilter
- handle方法
- 获取当前请求使用的路由Route
- 获取路由配置的过滤器集合route.getFilters()
- 合并全过滤器与路由配置过滤器combined
- 对过滤器排序AnnotationAwareOrderComparator.sort
- 通过过滤器集合构建顶级链表DefaultGatewayFilterChain,并对其当前请求调用链表的filter方法。
小结
Spring-Cloud-Gateway的过滤器接口分为两种:
- GlobalFilter : 全局过滤器,不需要在配置文件中配置,作用在所有的路由上,最终通过GatewayFilterAdapter包装成GatewayFilterChain可识别的过滤器
- GatewayFilter : 需要通过spring.cloud.routes.filters 配置在具体路由下,只作用在当前路由上或通过spring.cloud.default-filters配置在全局,作用在所有路由上
五、GatewayFilterFactory 配置路由过滤器
1️⃣、加载GatewayFilter
在路由定位器中以及看到了通过路由定义转换路由方法,其中包含了通过过滤器定义(FilterDefinition)转换过滤器(GatewayFilter)的部分,在RouteDefinitionRouteLocator类中源码如下:
/**
* 加载过滤器,根据过滤器的定义加载
*/
@SuppressWarnings("unchecked")
private List<GatewayFilter> loadGatewayFilters(String id, List<FilterDefinition> filterDefinitions) {
//遍历过滤器定义,将过滤器定义转换成对应的过滤器
List<GatewayFilter> filters = filterDefinitions.stream()
.map(definition -> {
//通过过滤器定义名称获取过滤器创建工厂
GatewayFilterFactory factory = this.gatewayFilterFactories.get(definition.getName());
if (factory == null) {
throw new IllegalArgumentException("Unable to find GatewayFilterFactory with name " + definition.getName());
}
//获取参数
Map<String, String> args = definition.getArgs();
if (logger.isDebugEnabled()) {
logger.debug("RouteDefinition " + id + " applying filter " + args + " to " + definition.getName());
} //根据args组装配置信息
Map<String, Object> properties = factory.shortcutType().normalize(args, factory, this.parser, this.beanFactory);
//构建过滤器创建配置信息
Object configuration = factory.newConfig();
ConfigurationUtils.bind(configuration, properties,
factory.shortcutFieldPrefix(), definition.getName(), validator); //通过过滤器工厂创建GatewayFilter
GatewayFilter gatewayFilter = factory.apply(configuration);
if (this.publisher != null) {
//发布事件
this.publisher.publishEvent(new FilterArgsEvent(this, id, properties));
}
return gatewayFilter;
})
.collect(Collectors.toList()); ArrayList<GatewayFilter> ordered = new ArrayList<>(filters.size());
//包装过滤器使其所有过滤器继承Ordered属性,可进行排序
for (int i = 0; i < filters.size(); i++) {
GatewayFilter gatewayFilter = filters.get(i);
if (gatewayFilter instanceof Ordered) {
ordered.add(gatewayFilter);
}
else {
ordered.add(new OrderedGatewayFilter(gatewayFilter, i + 1));
}
} return ordered;
} /**
* 获取RouteDefinition中的过滤器集合
*/
private List<GatewayFilter> getFilters(RouteDefinition routeDefinition) {
List<GatewayFilter> filters = new ArrayList<>(); //校验gatewayProperties是否含义默认的过滤器集合
//TODO: support option to apply defaults after route specific filters?
if (!this.gatewayProperties.getDefaultFilters().isEmpty()) {
//加载全局配置的默认过滤器集合
filters.addAll(loadGatewayFilters("defaultFilters",
this.gatewayProperties.getDefaultFilters()));
} if (!routeDefinition.getFilters().isEmpty()) {
//加载路由定义中的过滤器集合
filters.addAll(loadGatewayFilters(routeDefinition.getId(), routeDefinition.getFilters()));
} //排序
AnnotationAwareOrderComparator.sort(filters);
return filters;
}
- getFilters方法 合并配置中的全局过滤器与路由自身配置的过滤器,并对其排序(全局配置过滤器信息通过gatewayProperties.getDefaultFilters()获取)
- loadGatewayFilters 依次遍历路由定义下的FilterDefinition并将其通过对应的GatewayFilterFactory转换为GatewayFilter对象。
子类及其划分
3️⃣、AddResponseHeaderGatewayFilterFactory 创建解析
/**
*
* 响应header添加数据过滤器
* 用户在response header中添加配置数据
*/
public class AddResponseHeaderGatewayFilterFactory extends AbstractNameValueGatewayFilterFactory { @Override
public GatewayFilter apply(NameValueConfig config) {
return (exchange, chain) -> {
//获取Response并将配置的数据添加到header中
exchange.getResponse().getHeaders().add(config.getName(), config.getValue()); return chain.filter(exchange);
};
}
}。
配置示例:
spring:
cloud:
gateway:
default-filters:
- AddResponseHeader=X-Response-Default-Foo, Default-Bar
- AddResponseHeader=X-Response-Default-Foo, Default-Bar 会被解析成FilterDefinition对象 (name =AddResponseHeader ,args= [X-Response-Default-Foo,Default-Bar])
- 通FilterDefinition的Name找到AddResponseHeaderGatewayFilterFactory工厂
- 通过FilterDefinition 的args 创建Config对象(name=X-Response-Default-Foo,value=Default-Bar)
- 通过 AddResponseHeaderGatewayFilterFactory工厂的apply方法传入config创建GatewayFilter对象。
4️⃣、全部配置
5.1、请求头
spring:
cloud:
gateway:
routes:
- id: add_request_header_route
uri: http://example.org
filters:
- AddRequestHeader=X-Request-Foo, Bar
名称和值,这将为所有匹配请求的下游请求标头添加X-Request-Foo:Bar标头。
移除请求头
filters:
- RemoveRequestHeader=X-Request-Foo
5.2、请求参数
filters:
- AddRequestParameter=foo, bar
这会将foo = bar添加到下游请求的所有匹配请求的查询字符串中。
5.3、添加响应头
filters:
- AddResponseHeader=X-Response-Foo, Bar
这会将X-Response-Foo:Bar标头添加到所有匹配请求的下游响应标头中。
移除响应头
filters:
- RemoveResponseHeader=X-Response-Foo
设置响应头
filters:
- SetResponseHeader=X-Response-Foo, Bar
此GatewayFilter将替换具有给定名称的所有标头,而不是添加。
5.4、路径前缀
filters:
- PrefixPath=/mypath
这将使/ mypath前缀为所有匹配请求的路径。所以对/ hello的请求会被发送到/ mypath / hello。
5.5、原始主机头
没有参数,此过滤器设置路由过滤器将检查的请求属性,以确定是否应发送原始主机头,而不是http客户端确定的主机头。
filters:
- PreserveHostHeader
5.6、重定向
filters:
- RedirectTo=302, http://acme.org
这将发送带有Location:http://acme.org标头的状态302以执行重定向。
5.7、重写路径
predicates:
- Path=/foo/**
filters:
- RewritePath=/foo/(?<segment>.*), /$\{segment}
对于/ foo / bar的请求路径,这将在发出下游请求之前将路径设置为/ bar。注意由于YAML规范,$ \替换为$。
5.8、保存Session
predicates:
- Path=/foo/**
filters:
- SaveSession
5.9、路径模板
SetPath GatewayFilter Factory采用路径模板参数。它提供了一种通过允许模板化路径段来操作请求路径的简单方法。
predicates:
- Path=/foo/{segment}
filters:
- SetPath=/{segment}
对于/ foo / bar的请求路径,这将在发出下游请求之前将路径设置为/ bar。
5.10、设置响应状态
spring:
cloud:
gateway:
routes:
- id: setstatusstring_route
uri: http://example.org
filters:
- SetStatus=BAD_REQUEST
- id: setstatusint_route
uri: http://example.org
filters:
- SetStatus=401
5.11、请求参数剥离
parts参数指示在将请求发送到下游之前从请求中剥离的路径中的部分数。
predicates:
- Path=/name/**
filters:
- StripPrefix=2
当通过网关向/ name / bar / foo发出请求时,对nameservice的请求将类似于http:// nameservice / foo。
5.12、重试
retries:重试:应该尝试的重试次数
statuses:状态:应该重试的HTTP状态代码,使用org.springframework.http.HttpStatus表示
methods:方法:应该重试的HTTP方法,使用org.springframework.http.HttpMethod表示
series:系列:要重试的状态代码系列,使用org.springframework.http.HttpStatus.Series表示
routes:
- id: retry_test
uri: http://localhost:8080/flakey
predicates:
- Host=*.retry.com
filters:
- name: Retry
args:
retries: 3
statuses: BAD_GATEWAY
5.13、Hystrix GatewayFilter Factory
5.14、请求限速
RequestRateLimiter GatewayFilter Factory
5.15、安全头
009-spring cloud gateway-过滤器GatewayFilter、GlobalFilter、GatewayFilterChain、作用、生命周期、GatewayFilterFactory内置过滤器的更多相关文章
- Spring Cloud Gateway自定义过滤器实战(观测断路器状态变化)
欢迎访问我的GitHub https://github.com/zq2599/blog_demos 内容:所有原创文章分类汇总及配套源码,涉及Java.Docker.Kubernetes.DevOPS ...
- 快速突击 Spring Cloud Gateway
认识 Spring Cloud Gateway Spring Cloud Gateway 是一款基于 Spring 5,Project Reactor 以及 Spring Boot 2 构建的 API ...
- Spring Cloud Gateway(十一):全局过滤器GlobalFilter
本文基于 spring cloud gateway 2.0.1 1.简介 GlobalGilter 全局过滤器接口与 GatewayFilter 网关过滤器接口具有相同的方法定义.全局过滤器是一系列特 ...
- Spring Cloud Gateway(九):网关过滤器 GatewayFilter
本文基于 spring cloud gateway 2.0.1 1.简介 GatewayFilter 网关过滤器用于拦截并链式处理web请求,可以实现横切的与应用无关的需求,比如:安全.访问超时的设置 ...
- Spring Cloud gateway 三 自定义过滤器GatewayFilter
之前zuul 网关介绍.他有过滤器周期是四种,也是四种类型的过滤器.而gateway 只有俩种过滤器:"pre" 和 "post". PRE: 这种过滤器在请求 ...
- Spring Cloud gateway 网关服务二 断言、过滤器
微服务当前这么火爆的程度,如果不能学会一种微服务框架技术.怎么能升职加薪,增加简历的筹码?spring cloud 和 Dubbo 需要单独学习.说没有时间?没有精力?要学俩个框架?而Spring C ...
- Spring Cloud Alibaba学习笔记(21) - Spring Cloud Gateway 自定义全局过滤器
在前文中,我们介绍了Spring Cloud Gateway内置了一系列的全局过滤器,本文介绍如何自定义全局过滤器. 自定义全局过滤需要实现GlobalFilter 接口,该接口和 GatewayFi ...
- spring cloud gateway自定义过滤器
在API网关spring cloud gateway和负载均衡框架ribbon实战文章中,主要实现网关与负载均衡等基本功能,详见代码.本节内容将继续围绕此代码展开,主要讲解spring cloud g ...
- Spring Cloud Gateway GatewayFilter的使用
Spring Cloud Gateway GatewayFilter的使用 一.GatewayFilter的作用 二.Spring Cloud Gateway内置的 GatewayFilter 1.A ...
随机推荐
- springbatch---->springbatch的使用(二)
这里我们对springbatch做一个比较深入的学习例子,解压文件,读取文件内容过滤写入到数据库中.如果你掉进了黑暗里,你能做的,不过是静心等待,直到你的双眼适应黑暗. springbatch的使用案 ...
- icon VS html特殊字符
好久没来了,最近项目很多,今天要说的是个页面上用到的icon. 话“icon” 现在有很多icon库,我们再也不用切图来适配不同的分辨率了,但是对于新手来说,查阅icon库来找到适合的icon,实在费 ...
- Ajax技术(WEB无刷新提交数据)
(转自:http://www.jb51.net/article/291.htm) Ajax内部交流文档一.使用Ajax的主要原因 1.通过适当的Ajax应用达到更好的用户体验: 2.把以前的一些服务器 ...
- 题目1002:Grading(题目背景基于高考打分的简单判断)
题目链接:http://ac.jobdu.com/problem.php?pid=1002 详解链接:https://github.com/zpfbuaa/JobduInCPlusPlus 参考代码: ...
- 实战BRTSvc一款我见过的最嚣张的挖矿软件
第一步:发现告警 Suricata发现特征字符串jsonrpc,这个是匹配挖矿木马的一个重要特征.于是开始分析告警信息: 告警中可以提取出的有效信息如下: 目标IP:149.28.199.108 目标 ...
- matlab中 数据保留有效位数
可以使用round函数 ,这函数原本功能是四舍五入 比如: >> A = 0.0326465;>> B = round(A*1000)/1000 B = 0.0330
- 【CF687D】Dividing Kingdom II 线段树+并查集
[CF687D]Dividing Kingdom II 题意:给你一张n个点m条边的无向图,边有边权$w_i$.有q个询问,每次给出l r,问你:如果只保留编号在[l,r]中的边,你需要将所有点分成两 ...
- wpgcms---首页数据怎么掉
在首页调用单页模型下的,单页列表使用的方法: {% for page in wpg.page.getList('business') %} <li> <div class=" ...
- yii---load怎么使用
在用YII进行二次开发的时候,看到登录方法有一个load的方法: public function actionLogin() { if (Yii::$app->request->isPos ...
- Pointer Lock
Pointer Lock API 指针锁定(以前叫做 鼠标锁定) 提供了一种输入方法,这种方法是基于鼠标随着时间推移的运动的(也就是说,deltas),而不仅是鼠标光标的绝对位置.通过它可以访问原始的 ...