SOFA 源碼分析 —— 過濾器設計

前言

通常 Web 服務器在處理請求時汁讼,都會使用過濾器模式玄货,無論是 Tomcat 知给,還是 Netty瓤帚,過濾器的好處是能夠將處理的流程進行分離和解耦,比如一個 Http 請求進入服務器涩赢,可能需要解析 http 報頭戈次,權限驗證,國際化處理等等筒扒,過濾器可以很好的將這些過程隔離怯邪,并且,過濾器可以隨時卸載花墩,安裝悬秉。

每個 Web 服務器的過濾器思想都是類似的,只是實現(xiàn)方式略有不同冰蘑。

比如 Tomcat和泌,Tomcat 使用了一個 FilterChain 對象保存了所有的 filter,通過循環(huán)所有 filter 來完成過濾處理祠肥。關于 Tomcat 的過濾器源碼請看樓主之前的文章:
深入理解 Tomcat(九)源碼剖析之請求過程

Netty 使用了 pipeline 作為過濾器管道武氓,管道中使用 handler 做攔截處理,而 handler 使用一個 handlerInvoker(Context) 做隔離處理仇箱,也就是將 handler 和 handler 隔離開來县恕,中間使用 這個 Context 上下文進行流轉。關于 Netty 的 pipeline 可以查看樓主之前的文章 :
Netty 核心組件 Pipeline 源碼分析(一)之剖析 pipeline 三巨頭
Netty 核心組件 Pipeline 源碼分析(二)一個請求的 pipeline 之旅

而 SOFA 使用了和上面的兩個略有不同剂桥,我們今天通過源碼分析一下忠烛。

設計

SOFA 的過濾器由 3 個主要的類組成:

  1. FilterInvoker 過濾器包裝的Invoker對象,主要是隔離了filter和service的關系权逗;
  2. Filter 過濾器(可通過 SPI 擴展)
  3. FilterChain 過濾器鏈起始接口美尸,其實就是一個 Invoker垒拢。

我們看看這 3 個類的主要方法,就知道如何設計的了火惊。

Filter 主要方法:

public abstract SofaResponse invoke(FilterInvoker invoker, SofaRequest request) throws SofaRpcException;

invoke 方法求类,是一個抽象方法,用戶可以自己實現(xiàn)屹耐,而方法體就是用戶的處理邏輯尸疆。通常這個方法的結尾是:

return invoker.invoke(request);

調(diào)用了參數(shù) invoker 對象的 invoke 方法。我們看看這個 FilterInvoker 惶岭。

FilterInvoker 主要方法

構造方法:

public FilterInvoker(Filter nextFilter, FilterInvoker invoker, AbstractInterfaceConfig config) {
    this.nextFilter = nextFilter;
    this.invoker = invoker;
    this.config = config;
    if (config != null) {
        this.configContext = config.getConfigValueCache(false);
    }
}

樓主這里介紹一下他的主要構造方法寿弱。傳入一個 filter,一個 invoker按灶。

這個 filter 就是當前 invoker 包裝的過濾器症革,而參數(shù) invoker 就是他的下一個 invoker 節(jié)點。當執(zhí)行 FilterInvoker 的 invoke 方法的時候鸯旁,通常會調(diào)用 filter 的 invoke 方法噪矛,并傳入 invoker 參數(shù)。

這就回到我們上面分析的 filter 的 invoke 方法铺罢,該方法內(nèi)部會調(diào)用 invoker 的 invoke 方法艇挨,完成一次輪回。

再看看 FilterChain 韭赘。

FilterChain 主要方法

FilterChain 是框架直接操作的實例缩滨,每個調(diào)用者都間接持有一個 FilterChain 實例,而這個實例相當于過濾器鏈表的頭節(jié)點泉瞻。

構造方法:

protected FilterChain(List<Filter> filters, FilterInvoker lastInvoker, AbstractInterfaceConfig config) {
    // 調(diào)用過程外面包裝多層自定義filter
    // 前面的過濾器在最外層
    invokerChain = lastInvoker;
    if (CommonUtils.isNotEmpty(filters)) {
        loadedFilters = new ArrayList<Filter>();
        for (int i = filters.size() - 1; i >= 0; i--) {// 從最大的開始脉漏,從小到大開始執(zhí)行
            Filter filter = filters.get(i);
            if (filter.needToLoad(invokerChain)) {
                invokerChain = new FilterInvoker(filter, invokerChain, config);
                // cache this for filter when async respond
                loadedFilters.add(filter);
            }
        }
    }
}

在構造過濾器鏈的時候,會傳入一個過濾器數(shù)組袖牙,并傳入一個 FilterInvoker侧巨,這個 Invoker 是真正的業(yè)務方法,框架會在該 invoke 方法中反射調(diào)用接口的實現(xiàn)類贼陶,也就是業(yè)務代碼钮追。

上面的構造方法主要邏輯是:

倒序循環(huán) List 中的 Filter 實例坪稽,將 Filter 用 FilterInvoker 封裝拒课,并傳入上一個 FilterInvoker 到 FilterInvoker 的構造方法中峰档,形成鏈表。而單獨傳入的 FilterInvoker 則會放到最后一個節(jié)點撮胧。`

所以桨踪,最終,當 FilterChain 調(diào)用過濾器鏈的時候芹啥,會從 order 最小的過濾器開始锻离,最后執(zhí)行業(yè)務方法铺峭。

注意:SOFA 過濾器中,真正執(zhí)行業(yè)務方法的不是 Filter汽纠,而是 FilterInvoker 的具體實現(xiàn)類卫键,在 invoke 方法中,會反射調(diào)用接口實現(xiàn)類的方法虱朵。原因是過濾器最后調(diào)用的 invoker.invoke莉炉。就不用再構造一個 filter 了。

以上就是 SOFA 的過濾器設計碴犬。從總體上來講絮宁,和 Tomcat 的 過濾器類似,只是 Tomcat 使用的數(shù)組服协,并且將 Service 區(qū)分看待绍昂,即執(zhí)行完所有的過濾器后,執(zhí)行 Service偿荷。而 SOFA 使用的是一個鏈表窘游,并沒有區(qū)分對待 Service。

One more thing

Filter 是個接口遭顶,并且標注了 @Extensible(singleton = false) 注解张峰,表示這是一個擴展點泪蔫,這個是 SOFA 微內(nèi)核的一個設計棒旗。所有的中間件都可以通過擴展點加入到框架中。

而擴展點其實有點類似 Spring 的 Bean撩荣,Spring Bean 和核心數(shù)據(jù)結構是 BeanDefine铣揉,SOFA 的 擴展點核心數(shù)據(jù)結構則是 ExtensionClass,該類定義了擴展點的所有相關信息餐曹。

SOFA 會將所有的擴展點放在一個 ExtensionLoader 的 ConcurrentHashMap<String, ExtensionClass<T>> 中逛拱。

ExtensionLoader 可以稱之為擴展類加載器,一個 ExtensionLoader 對應一個可擴展的接口台猴。

總結

從設計上來說朽合,SOFA 的過濾器更類似 Tomcat 的過濾器,相對于 Netty 的過濾器各有特色饱狂。Netty 的過濾器可以隨時插拔曹步,也許從業(yè)務上來說,SOFA 并不需要這樣的功能吧休讳。

而同時讲婚,F(xiàn)ilter 基于 SOFA 的擴展點來的。Dubbo 作者說過:

大凡發(fā)展的比較好的框架俊柔,都遵守微核的理念筹麸,
Eclipse的微核是OSGi活合, Spring的微核是BeanFactory,Maven的微核是Plexus物赶,
通常核心是不應該帶有功能性的白指,而是一個生命周期和集成容器,
這樣各功能可以通過相同的方式交互及擴展酵紫,并且任何功能都可以被替換侵续,
如果做不到微核,至少要平等對待第三方憨闰,
即原作者能實現(xiàn)的功能状蜗,擴展者應該可以通過擴展的方式全部做到,
原作者要把自己也當作擴展者鹉动,這樣才能保證框架的可持續(xù)性及由內(nèi)向外的穩(wěn)定性轧坎。

微核插件式,平等對待第三方 對于框架來說泽示,非常重要缸血。

最后編輯于
?著作權歸作者所有,轉載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市械筛,隨后出現(xiàn)的幾起案子捎泻,更是在濱河造成了極大的恐慌,老刑警劉巖埋哟,帶你破解...
    沈念sama閱讀 211,376評論 6 491
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件笆豁,死亡現(xiàn)場離奇詭異,居然都是意外死亡赤赊,警方通過查閱死者的電腦和手機闯狱,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,126評論 2 385
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來抛计,“玉大人哄孤,你說我怎么就攤上這事〈到兀” “怎么了瘦陈?”我有些...
    開封第一講書人閱讀 156,966評論 0 347
  • 文/不壞的土叔 我叫張陵,是天一觀的道長波俄。 經(jīng)常有香客問我晨逝,道長,這世上最難降的妖魔是什么弟断? 我笑而不...
    開封第一講書人閱讀 56,432評論 1 283
  • 正文 為了忘掉前任咏花,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘昏翰。我一直安慰自己苍匆,他們只是感情好,可當我...
    茶點故事閱讀 65,519評論 6 385
  • 文/花漫 我一把揭開白布棚菊。 她就那樣靜靜地躺著浸踩,像睡著了一般。 火紅的嫁衣襯著肌膚如雪统求。 梳的紋絲不亂的頭發(fā)上检碗,一...
    開封第一講書人閱讀 49,792評論 1 290
  • 那天,我揣著相機與錄音码邻,去河邊找鬼折剃。 笑死,一個胖子當著我的面吹牛像屋,可吹牛的內(nèi)容都是我干的怕犁。 我是一名探鬼主播,決...
    沈念sama閱讀 38,933評論 3 406
  • 文/蒼蘭香墨 我猛地睜開眼己莺,長吁一口氣:“原來是場噩夢啊……” “哼奏甫!你這毒婦竟也來了?” 一聲冷哼從身側響起凌受,我...
    開封第一講書人閱讀 37,701評論 0 266
  • 序言:老撾萬榮一對情侶失蹤阵子,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后胜蛉,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體挠进,經(jīng)...
    沈念sama閱讀 44,143評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,488評論 2 327
  • 正文 我和宋清朗相戀三年腾么,在試婚紗的時候發(fā)現(xiàn)自己被綠了奈梳。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,626評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡解虱,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出漆撞,到底是詐尸還是另有隱情殴泰,我是刑警寧澤,帶...
    沈念sama閱讀 34,292評論 4 329
  • 正文 年R本政府宣布浮驳,位于F島的核電站悍汛,受9級特大地震影響,放射性物質發(fā)生泄漏至会。R本人自食惡果不足惜离咐,卻給世界環(huán)境...
    茶點故事閱讀 39,896評論 3 313
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧宵蛀,春花似錦昆著、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,742評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至梧宫,卻和暖如春接谨,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背塘匣。 一陣腳步聲響...
    開封第一講書人閱讀 31,977評論 1 265
  • 我被黑心中介騙來泰國打工脓豪, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人忌卤。 一個月前我還...
    沈念sama閱讀 46,324評論 2 360
  • 正文 我出身青樓跑揉,卻偏偏與公主長得像,于是被迫代替她去往敵國和親埠巨。 傳聞我的和親對象是個殘疾皇子历谍,可洞房花燭夜當晚...
    茶點故事閱讀 43,494評論 2 348

推薦閱讀更多精彩內(nèi)容

  • 監(jiān)聽器(listener) 監(jiān)聽器簡介 :監(jiān)聽器就是一個實現(xiàn)特定接口的普通java程序,這個程序專門用于監(jiān)聽另一個...
    奮斗的老王閱讀 2,498評論 0 53
  • 實例 實現(xiàn)一個簡單的過濾器只需要兩步1辣垒,實現(xiàn)Filter接口寫一個過濾器實現(xiàn)類 2望侈,web.xml文件中新增相關f...
    禾邊的曉作坊閱讀 2,046評論 1 4
  • Spring Cloud為開發(fā)人員提供了快速構建分布式系統(tǒng)中一些常見模式的工具(例如配置管理,服務發(fā)現(xiàn)勋桶,斷路器脱衙,智...
    卡卡羅2017閱讀 134,629評論 18 139
  • 本文包括:1、Filter簡介2例驹、Filter是如何實現(xiàn)攔截的捐韩?3、Filter開發(fā)入門4鹃锈、Filter的生命周期...
    廖少少閱讀 7,256評論 3 56
  • 氤氳的熱氣翻騰著上升屎债,跳躍出火焰的形狀仅政,透明的乳白色,為閃爍著冰冷的金屬光芒的被子添上溫暖的注腳盆驹,夜色中圆丹,房間里的...
    塵上云端閱讀 109評論 0 0