【深入解析spring cloud gateway】09 巨坑!GlobalFilter的执行顺序

最近在写网关代码时,发现一个问题,是关于GlobalFilter的代码执行顺序的问题。

一、问题引出

在我的理解里面,我以为Filter的执行顺序是这么的:
在这里插入图片描述

以上的三个filter,从左到右的顺序执行。我认为的Filter的链式调用是这样的
执行顺序应该是:
pre0->pre1->pre2->post2->post2->post0
然而,实际顺序竟然不是这样的。还是以代码为示例吧:

二、演示GlobalFilter执行顺序

定义两个GlobalFilter,顺序分别为0和1,分别在chain.filter前后输出日志

@Slf4j
public class FirstFilter implements GlobalFilter, Ordered {
    
    
    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
    
    
        log.info("firstFilter start");
        Mono<Void> filter = chain.filter(exchange);
        log.info("firstFilter end");
        return filter;
    }

    @Override
    public int getOrder() {
    
    
        return 0;
    }
}
@Slf4j
public class SecondFilter implements GlobalFilter, Ordered {
    
    
    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
    
    
        log.info("secondFilter start");
        Mono<Void> filter = chain.filter(exchange);
        log.info("secondFilter end");
        return filter;
    }

    @Override
    public int getOrder() {
    
    
        return 1;
    }
}

先说一下,我以为的执行顺序:

firstFilter start
secondFilter start
secondFilter end
firstFilter end

然后请求一下,看一下日志
日志输出如下:

firstFilter start
firstFilter end
secondFilter start
secondFilter end

纳尼?为会么会是这样?
现在的效果实际上是这样的:即每一个filter顺序执行,执行完第一个filter,再执行第2个filter
在这里插入图片描述

三、源码分析

再分析一下源码
chain.filter(exchange)到底是怎么执行的
chain对应的实现类是:DefaultGatewayFilterChain

@Override
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方法,并不是用代码直接调用下一个filter!而是返回的一个Mono对象!
Mono里面定义的方法,并不会马上执行,而是在subscribe方法执行后,才会运行。

这里有点难以理解,可以反复体会一下。

四、顺序执行带来的问题

现在我们知道了,filter是按顺序执行的,而不是我们想象中的pre1-pre2->post2->post1这种顺序。
现在有一个问题,比如我想在第一个filter,最开始比如用ThreadLocal设置一下用户的上下文信息,然后中间的filter就可以获取到上下文信息了。最后在清除用户上下文信息。这个要怎么做到?
示例代码如下:
定义一个Filter,顺序在最前面
通过Mono设置两个回调方法doFirst和doFinally

@Slf4j
public class ContextSetFilter implements GlobalFilter, Ordered {
    
    
    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
    
    
        return chain.filter(exchange)
                .doFirst(() -> log.info("手动设置一下threadLocal上下文"))
                .doFinally(signalType -> log.info("手动清除一下threadLocal上下文"));
    }

    @Override
    public int getOrder() {
    
    
        return -1;
    }
}

再来看一下日志执行的结果,分别在最开始和最后,执行了上下文的处理。

手动设置一下threadLocal上下文
firstFilter start
firstFilter end
secondFilter start
secondFilter end
手动清除一下threadLocal上下文

源码地址:https://gitee.com/syk1234/spring-cloud-new-demo.git

猜你喜欢

转载自blog.csdn.net/suyuaidan/article/details/132744548
今日推荐