準(zhǔn)備工作
需要了解響應(yīng)式編程,推薦閱讀
版本
Spring Cloud Gateway:2.2.3.RELEASE
本文目標(biāo)
了解 Gateway Filter 內(nèi)部執(zhí)行原理
問題:
@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;
}
}
我編寫了一個(gè) TestGlobalFilter,下一個(gè) Filter 的邏輯是輸出日志 HelloWorld。日志中輸出的順序會(huì)是什么樣?
正確答案是
start
end
HelloWorld
如果按照 Servlet 的開發(fā)思想,調(diào)用 chain.filter 一定會(huì)立刻執(zhí)行下一個(gè) Filter,Gateway 為什么不可以呢?
因?yàn)?chain.filter 的返回值是 Mono,必須要有訂閱者調(diào)用 subscribe 后才會(huì)執(zhí)行發(fā)布者邏輯
DefaultGatewayFilterChain
我們來看下 DefaultGatewayFilterChain 的代碼

DefaultGatewayFilterChain 返回的是一個(gè) MonoDefer。其內(nèi)部包含了調(diào)用下一個(gè) Filter 的內(nèi)部函數(shù),那么這個(gè)邏輯怎樣才能觸發(fā)的呢?下面繼續(xù)來看 MonoDefer 的源碼
MonoDefer

MonoDefer subscribe 邏輯如下
- 調(diào)用
supplier.get(),執(zhí)行內(nèi)部函數(shù)的命令式代碼,執(zhí)行結(jié)束后,內(nèi)部函數(shù)會(huì)返回一個(gè) Mono -
p.subscribe(actual);訂閱內(nèi)部函數(shù)返回的 Mono
當(dāng) supplier.get() 拋出異常時(shí),首先向訂閱者傳遞一個(gè)空的 Subscription,然后再傳遞異常

MonoDefer 雖然也是發(fā)布者,但是他只是在真正的發(fā)布者和訂閱者之間做一個(gè)承載的作用
過濾器鏈刨析
在理解了上述兩個(gè)類之后,我們現(xiàn)在可以梳理一下 Gateway 過濾器鏈的執(zhí)行邏輯了
雖然從 Gateway 接收到請(qǐng)求到過濾器鏈中間還會(huì)經(jīng)歷很多步驟,這里我們?yōu)榱朔奖憷斫?,直接把過濾器鏈的調(diào)用方,抽象為一個(gè)訂閱者(因?yàn)樽罱K過濾器鏈會(huì)返回一個(gè) Publisher)
除此之外,再簡(jiǎn)化一下 Filter 的返回值。正常來說 Filter 可以返回任何響應(yīng)式的發(fā)布者邏輯,我們這里簡(jiǎn)化為每個(gè) Filter 都返回 chain.filter (將最簡(jiǎn)單的流程理解后,其實(shí)復(fù)雜的響應(yīng)式返回也是大同小異)
訂閱者請(qǐng)求 First Filter,這里首先會(huì)執(zhí)行
filter方法中所有的命令式的代碼(響應(yīng)式的代碼并不會(huì)執(zhí)行,因?yàn)?Mono 并沒有被消費(fèi))訂閱者調(diào)用 Filter 返回的 MonoDefer 的 subscribe 方法。MonoDefer 被訂閱時(shí)首先會(huì)執(zhí)行內(nèi)部函數(shù)。如果還有下一個(gè)過濾器,則執(zhí)行并返回
nextFilter.filter,如果所有過濾器都已執(zhí)行完畢則返回 Mono.empty(對(duì)應(yīng) MonoDefer 的 44 行)nextFilter.filter先執(zhí)行filter方法中所有的命令式的代碼,然后返回chain.filterFirst Filter 返回的 MonoDefer 內(nèi)部會(huì)去訂閱
nextFilter.filter返回的 Mono(對(duì)應(yīng) MonoDefer 的 52 行)。Second MonoDefer(nextFilter.filter的返回值)被訂閱,接下來就是重復(fù)步驟 2 的邏輯,無限套娃下去直到所有 Filter 執(zhí)行完畢...
下面用一張圖來解釋一下上面的邏輯

通過上述的圖文講解,我們可以看到響應(yīng)式編程中一個(gè)過濾器鏈該怎么設(shè)計(jì)和實(shí)現(xiàn)
回到問題
回到最開始的問題,如果想在 Spring Cloud Gateway 中實(shí)現(xiàn)先執(zhí)行過濾器鏈再執(zhí)行某某操作,應(yīng)該怎么寫呢?
@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 的作用就是內(nèi)部消費(fèi)并忽略第一個(gè) Mono(但是 Error 信號(hào)會(huì)被傳遞下去),然后入?yún)⒌?Mono 作為生產(chǎn)者向下游傳播數(shù)據(jù)。
忽略了 chain.filter 返回的 Mono 不會(huì)造成問題嗎?當(dāng)然不會(huì),Gateway 的 Filter 鏈的訂閱者并不需要我們傳遞什么數(shù)據(jù),我們只需要將所有過濾器代碼執(zhí)行完即可
最后
如果覺得我的文章對(duì)你有幫助,動(dòng)動(dòng)小手點(diǎn)下喜歡和關(guān)注,你的支持是對(duì)我最大的幫助