准备工作
需要了解响应式编程,推荐阅读
版本
Spring Cloud Gateway:2.2.3.RELEASE
本文目标
了解 Gateway Filter 内部执行原理
问题:
@Component
public class TestGlobalFilter implements GlobalFilter, Ordered {
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
log.info("start");
Mono<Void> mono = chain.filter(exchange);
log.info("end")
return mono;
}
@Override
public int getOrder() {
return 1;
}
}
我编写了一个 TestGlobalFilter,下一个 Filter 的逻辑是输出日志 HelloWorld
。日志中输出的顺序会是什么样?
正确答案是
start
end
HelloWorld
如果按照 Servlet 的开发思想,调用 chain.filter
一定会立刻执行下一个 Filter,Gateway 为什么不可以呢?
因为 chain.filter
的返回值是 Mono,必须要有订阅者调用 subscribe 后才会执行发布者逻辑
DefaultGatewayFilterChain
我们来看下 DefaultGatewayFilterChain
的代码
![](https://img.haomeiwen.com/i9949918/0a5a6dcffc0e5491.png)
DefaultGatewayFilterChain 返回的是一个 MonoDefer。其内部包含了调用下一个 Filter 的内部函数,那么这个逻辑怎样才能触发的呢?下面继续来看 MonoDefer 的源码
MonoDefer
![](https://img.haomeiwen.com/i9949918/0eb967d3dd3f7034.png)
MonoDefer subscribe 逻辑如下
- 调用
supplier.get()
,执行内部函数的命令式代码,执行结束后,内部函数会返回一个 Mono -
p.subscribe(actual);
订阅内部函数返回的 Mono
当 supplier.get()
抛出异常时,首先向订阅者传递一个空的 Subscription,然后再传递异常
![](https://img.haomeiwen.com/i9949918/caef935dc0bf606a.png)
MonoDefer 虽然也是发布者,但是他只是在真正的发布者和订阅者之间做一个承载的作用
过滤器链刨析
在理解了上述两个类之后,我们现在可以梳理一下 Gateway 过滤器链的执行逻辑了
虽然从 Gateway 接收到请求到过滤器链中间还会经历很多步骤,这里我们为了方便理解,直接把过滤器链的调用方,抽象为一个订阅者(因为最终过滤器链会返回一个 Publisher)
除此之外,再简化一下 Filter 的返回值。正常来说 Filter 可以返回任何响应式的发布者逻辑,我们这里简化为每个 Filter 都返回 chain.filter
(将最简单的流程理解后,其实复杂的响应式返回也是大同小异)
-
订阅者请求 First Filter,这里首先会执行
filter
方法中所有的命令式的代码(响应式的代码并不会执行,因为 Mono 并没有被消费) -
订阅者调用 Filter 返回的 MonoDefer 的 subscribe 方法。MonoDefer 被订阅时首先会执行内部函数。如果还有下一个过滤器,则执行并返回
nextFilter.filter
,如果所有过滤器都已执行完毕则返回 Mono.empty(对应 MonoDefer 的 44 行) -
nextFilter.filter
先执行filter
方法中所有的命令式的代码,然后返回chain.filter
-
First Filter 返回的 MonoDefer 内部会去订阅
nextFilter.filter
返回的 Mono(对应 MonoDefer 的 52 行)。Second MonoDefer(nextFilter.filter
的返回值)被订阅,接下来就是重复步骤 2 的逻辑,无限套娃下去直到所有 Filter 执行完毕...
下面用一张图来解释一下上面的逻辑
![](https://img.haomeiwen.com/i9949918/e44e19de397a78a1.png)
通过上述的图文讲解,我们可以看到响应式编程中一个过滤器链该怎么设计和实现
回到问题
回到最开始的问题,如果想在 Spring Cloud Gateway 中实现先执行过滤器链再执行某某操作,应该怎么写呢?
@Slf4j
@Component
public class LogFilter implements GlobalFilter, Ordered {
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
log.info("hello");
return chain.filter(exchange)
.then(Mono.defer(() -> {
log.info("world");
return Mono.empty();
}));
}
@Override
public int getOrder() {
return -9;
}
}
Mono.then
的作用就是内部消费并忽略第一个 Mono(但是 Error 信号会被传递下去),然后入参的 Mono 作为生产者向下游传播数据。
忽略了 chain.filter
返回的 Mono 不会造成问题吗?当然不会,Gateway 的 Filter 链的订阅者并不需要我们传递什么数据,我们只需要将所有过滤器代码执行完即可
最后
如果觉得我的文章对你有帮助,动动小手点下喜欢和关注,你的支持是对我最大的帮助
网友评论