準(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;
}
}
我編寫了一個 TestGlobalFilter瞭空,下一個 Filter 的邏輯是輸出日志 HelloWorld
。日志中輸出的順序會是什么樣伟骨?
正確答案是
start
end
HelloWorld
如果按照 Servlet 的開發(fā)思想,調(diào)用 chain.filter
一定會立刻執(zhí)行下一個 Filter撵摆,Gateway 為什么不可以呢底靠?
因?yàn)?chain.filter
的返回值是 Mono,必須要有訂閱者調(diào)用 subscribe 后才會執(zhí)行發(fā)布者邏輯
DefaultGatewayFilterChain
我們來看下 DefaultGatewayFilterChain
的代碼
DefaultGatewayFilterChain 返回的是一個 MonoDefer特铝。其內(nèi)部包含了調(diào)用下一個 Filter 的內(nèi)部函數(shù)暑中,那么這個邏輯怎樣才能觸發(fā)的呢?下面繼續(xù)來看 MonoDefer 的源碼
MonoDefer
MonoDefer subscribe 邏輯如下
- 調(diào)用
supplier.get()
鲫剿,執(zhí)行內(nèi)部函數(shù)的命令式代碼鳄逾,執(zhí)行結(jié)束后,內(nèi)部函數(shù)會返回一個 Mono -
p.subscribe(actual);
訂閱內(nèi)部函數(shù)返回的 Mono
當(dāng) supplier.get()
拋出異常時(shí)灵莲,首先向訂閱者傳遞一個空的 Subscription雕凹,然后再傳遞異常
MonoDefer 雖然也是發(fā)布者,但是他只是在真正的發(fā)布者和訂閱者之間做一個承載的作用
過濾器鏈刨析
在理解了上述兩個類之后,我們現(xiàn)在可以梳理一下 Gateway 過濾器鏈的執(zhí)行邏輯了
雖然從 Gateway 接收到請求到過濾器鏈中間還會經(jīng)歷很多步驟枚抵,這里我們?yōu)榱朔奖憷斫庀哂苯影堰^濾器鏈的調(diào)用方,抽象為一個訂閱者(因?yàn)樽罱K過濾器鏈會返回一個 Publisher)
除此之外汽摹,再簡化一下 Filter 的返回值李丰。正常來說 Filter 可以返回任何響應(yīng)式的發(fā)布者邏輯,我們這里簡化為每個 Filter 都返回 chain.filter
(將最簡單的流程理解后逼泣,其實(shí)復(fù)雜的響應(yīng)式返回也是大同小異)
訂閱者請求 First Filter趴泌,這里首先會執(zhí)行
filter
方法中所有的命令式的代碼(響應(yīng)式的代碼并不會執(zhí)行,因?yàn)?Mono 并沒有被消費(fèi))訂閱者調(diào)用 Filter 返回的 MonoDefer 的 subscribe 方法拉庶。MonoDefer 被訂閱時(shí)首先會執(zhí)行內(nèi)部函數(shù)嗜憔。如果還有下一個過濾器,則執(zhí)行并返回
nextFilter.filter
氏仗,如果所有過濾器都已執(zhí)行完畢則返回 Mono.empty(對應(yīng) MonoDefer 的 44 行)nextFilter.filter
先執(zhí)行filter
方法中所有的命令式的代碼吉捶,然后返回chain.filter
First Filter 返回的 MonoDefer 內(nèi)部會去訂閱
nextFilter.filter
返回的 Mono(對應(yīng) MonoDefer 的 52 行)。Second MonoDefer(nextFilter.filter
的返回值)被訂閱廓鞠,接下來就是重復(fù)步驟 2 的邏輯帚稠,無限套娃下去直到所有 Filter 執(zhí)行完畢...
下面用一張圖來解釋一下上面的邏輯
通過上述的圖文講解,我們可以看到響應(yīng)式編程中一個過濾器鏈該怎么設(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)并忽略第一個 Mono(但是 Error 信號會被傳遞下去)砌们,然后入?yún)⒌?Mono 作為生產(chǎn)者向下游傳播數(shù)據(jù)杆麸。
忽略了 chain.filter
返回的 Mono 不會造成問題嗎?當(dāng)然不會浪感,Gateway 的 Filter 鏈的訂閱者并不需要我們傳遞什么數(shù)據(jù)昔头,我們只需要將所有過濾器代碼執(zhí)行完即可
最后
如果覺得我的文章對你有幫助,動動小手點(diǎn)下喜歡和關(guān)注影兽,你的支持是對我最大的幫助