dubbo的Filter機(jī)制

1.dubbo調(diào)用過程

以dubbo官方demo為例僚纷,在provider端矩距,從netty接收到消息,遞交給業(yè)務(wù)線程池處理開始怖竭,到真正調(diào)用到業(yè)務(wù)方法sayHello()結(jié)束锥债,中間經(jīng)過了十幾個(gè)Filter的處理。見下圖


屏幕快照 2019-08-04 下午1.04.21.png

那么這些Filter是如何初始化的痊臭,調(diào)用的時(shí)候又是如何執(zhí)行的呢哮肚?接下來一步一步介紹。

2.dubbo服務(wù)導(dǎo)出過程

dubbo在進(jìn)行服務(wù)導(dǎo)出時(shí)主要做了如下一些工作


屏幕快照 2019-08-04 下午12.55.24.png

可以看到:第二步中核心工作就包括Filter的初始化广匙。見下ProtocolFilterWrapper#export方法

public <T> Exporter<T> export(Invoker<T> invoker) throws RpcException {
        if (REGISTRY_PROTOCOL.equals(invoker.getUrl().getProtocol())) {
            return protocol.export(invoker);
        }
        return protocol.export(buildInvokerChain(invoker, SERVICE_FILTER_KEY, CommonConstants.PROVIDER));
    }

其中有一步是buildInvokerChain允趟,從名字上也可以看出,這是初始化一個(gè)責(zé)任鏈鸦致,對應(yīng)設(shè)計(jì)模式中的責(zé)任鏈模式拼窥。接著看這個(gè)責(zé)任鏈?zhǔn)窃趺闯跏蓟模≒rotocolFilterWrapper#buildInvokerChain)

private static <T> Invoker<T> buildInvokerChain(final Invoker<T> invoker, String key, String group) {
        Invoker<T> last = invoker;
        //利用dubbo spi得到實(shí)現(xiàn)了Filter接口的所有實(shí)例,形成List<Filter>
        List<Filter> filters = ExtensionLoader.getExtensionLoader(Filter.class).getActivateExtension(invoker.getUrl(), key, group);

        if (!filters.isEmpty()) {
            //遍歷這個(gè)List<Filter>蹋凝,形成一個(gè)Invoker鏈表
            for (int i = filters.size() - 1; i >= 0; i--) {
                final Filter filter = filters.get(i);
                final Invoker<T> next = last;
                last = new Invoker<T>() {

                    @Override
                    public Class<T> getInterface() {
                        return invoker.getInterface();
                    }

                    @Override
                    public URL getUrl() {
                        return invoker.getUrl();
                    }

                    @Override
                    public boolean isAvailable() {
                        return invoker.isAvailable();
                    }
                    //執(zhí)行責(zé)任鏈上某個(gè)節(jié)點(diǎn)的invoke邏輯時(shí),同時(shí)會(huì)傳入next節(jié)點(diǎn)信息总棵,以支持鏈?zhǔn)綀?zhí)行
                    @Override
                    public Result invoke(Invocation invocation) throws RpcException {
                        Result asyncResult;
                        try {
                            asyncResult = filter.invoke(next, invocation);
                        } catch (Exception e) {
                            // onError callback
                            if (filter instanceof ListenableFilter) {
                                Filter.Listener listener = ((ListenableFilter) filter).listener();
                                if (listener != null) {
                                    listener.onError(e, invoker, invocation);
                                }
                            }
                            throw e;
                        }
                        return asyncResult;
                    }

                    @Override
                    public void destroy() {
                        invoker.destroy();
                    }

                    @Override
                    public String toString() {
                        return invoker.toString();
                    }
                };
            }
        }

        return new CallbackRegistrationInvoker<>(last, filters);
    }

可以看到這個(gè)Filter的責(zé)任鏈初始化過程
1.通過dubbo spi機(jī)制鳍寂,取得所有Filter實(shí)例形成一個(gè)ArrayList<Filter>
2.遍歷這個(gè)ArrayList,以next指針初始化一個(gè)Invoker的鏈表InvokerList
3.Invoker執(zhí)行邏輯即執(zhí)行Filter的invoke方法的同時(shí)情龄,將next指針作為參數(shù)傳入迄汛,以支持鏈?zhǔn)秸{(diào)用

整個(gè)過程結(jié)束之后,會(huì)有兩個(gè)List骤视,一個(gè)ArrayList<Filter>鞍爱,一個(gè)以NEXT指針形成的InvokerList,這兩個(gè)List就是dubbo Filter機(jī)制的基礎(chǔ)专酗。

3.Filter接口

@SPI
public interface Filter {
    /**
     * Does not need to override/implement this method.
     */
    Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException;

     interface Listener {

        void onResponse(Result appResponse, Invoker<?> invoker, Invocation invocation);

        void onError(Throwable t, Invoker<?> invoker, Invocation invocation);
    }

}

Filter接口提供了一個(gè)invoke方法睹逃,另一個(gè)是Listener接口,有onResponse,onError兩個(gè)方法沉填。
這兩部分對應(yīng)著Filter對請求和響應(yīng)的處理邏輯
請求的處理以AccessLogFilter#invoke的實(shí)現(xiàn)為例,可以看到疗隶,其首先進(jìn)行AccessLog的處理,然后調(diào)用
invoker.invoke().這個(gè)invoker即之前Filter初始化的時(shí)候翼闹,以Next指針形成的那個(gè)InvokerList鏈表中的節(jié)點(diǎn)斑鼻。這樣一來,整個(gè)鏈表中的節(jié)點(diǎn)都會(huì)得到順序執(zhí)行猎荠。

@Override
    public Result invoke(Invoker<?> invoker, Invocation inv) throws RpcException {
        try {
            String accessLogKey = invoker.getUrl().getParameter(ACCESS_LOG_KEY);
            if (ConfigUtils.isNotEmpty(accessLogKey)) {
                AccessLogData logData = buildAccessLogData(invoker, inv);
                log(accessLogKey, logData);
            }
        } catch (Throwable t) {
            logger.warn("Exception in AccessLogFilter of service(" + invoker + " -> " + inv + ")", t);
        }
        return invoker.invoke(inv);
    }

響應(yīng)的處理可以見CallbackRegistrationInvoker#invoke

asyncResult = asyncResult.whenCompleteWithContext((r, t) -> {
                for (int i = filters.size() - 1; i >= 0; i--) {
                    Filter filter = filters.get(i);
                    // onResponse callback
                    if (filter instanceof ListenableFilter) {
                        Filter.Listener listener = ((ListenableFilter) filter).listener();
                        if (listener != null) {
                            if (t == null) {
                                listener.onResponse(r, filterInvoker, invocation);
                            } else {
                                listener.onError(t, filterInvoker, invocation);
                            }
                        }
                    } else {
                        filter.onResponse(r, filterInvoker, invocation);
                    }
                }
            });

之前Filter初始化的時(shí)候求晶,會(huì)形成兩個(gè)list,next指針形成的那個(gè)鏈表用于對請求的處理蚁孔,另一個(gè)ArrayList<Filter> 就是在此時(shí)執(zhí)行,拿到結(jié)果之后饮焦,遍歷這個(gè)ArrayList,執(zhí)行其onResponse或者onError方法拒垃,如此一來停撞,請求和響應(yīng)應(yīng)就會(huì)經(jīng)過所有生效的Filter處理。

總結(jié):dubbo的Filter機(jī)制是一種典型的責(zé)任鏈模式悼瓮,這個(gè)鏈的基礎(chǔ)即上述兩個(gè)list戈毒。如果我們在自己的業(yè)務(wù)場景中,需要對請求或者響應(yīng)做一些通用的處理横堡,那么也很簡單埋市,直接基于dubbo spi( http://www.reibang.com/p/d4d7ebc8f7bb
),自定義邏輯實(shí)現(xiàn)Filter接口的相應(yīng)方法即可命贴。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末道宅,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子胸蛛,更是在濱河造成了極大的恐慌污茵,老刑警劉巖,帶你破解...
    沈念sama閱讀 211,123評論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件葬项,死亡現(xiàn)場離奇詭異泞当,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)民珍,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,031評論 2 384
  • 文/潘曉璐 我一進(jìn)店門襟士,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人嚷量,你說我怎么就攤上這事陋桂。” “怎么了蝶溶?”我有些...
    開封第一講書人閱讀 156,723評論 0 345
  • 文/不壞的土叔 我叫張陵嗜历,是天一觀的道長。 經(jīng)常有香客問我,道長秸脱,這世上最難降的妖魔是什么落包? 我笑而不...
    開封第一講書人閱讀 56,357評論 1 283
  • 正文 為了忘掉前任,我火速辦了婚禮摊唇,結(jié)果婚禮上咐蝇,老公的妹妹穿的比我還像新娘。我一直安慰自己巷查,他們只是感情好有序,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,412評論 5 384
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著岛请,像睡著了一般旭寿。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上崇败,一...
    開封第一講書人閱讀 49,760評論 1 289
  • 那天盅称,我揣著相機(jī)與錄音,去河邊找鬼后室。 笑死缩膝,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的岸霹。 我是一名探鬼主播疾层,決...
    沈念sama閱讀 38,904評論 3 405
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼贡避!你這毒婦竟也來了痛黎?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,672評論 0 266
  • 序言:老撾萬榮一對情侶失蹤刮吧,失蹤者是張志新(化名)和其女友劉穎湖饱,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體杀捻,經(jīng)...
    沈念sama閱讀 44,118評論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡琉历,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,456評論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了水醋。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,599評論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡彪置,死狀恐怖拄踪,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情拳魁,我是刑警寧澤惶桐,帶...
    沈念sama閱讀 34,264評論 4 328
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響姚糊,放射性物質(zhì)發(fā)生泄漏贿衍。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,857評論 3 312
  • 文/蒙蒙 一救恨、第九天 我趴在偏房一處隱蔽的房頂上張望贸辈。 院中可真熱鬧,春花似錦肠槽、人聲如沸擎淤。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,731評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽嘴拢。三九已至,卻和暖如春寂纪,著一層夾襖步出監(jiān)牢的瞬間席吴,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,956評論 1 264
  • 我被黑心中介騙來泰國打工捞蛋, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留孝冒,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 46,286評論 2 360
  • 正文 我出身青樓襟交,卻偏偏與公主長得像迈倍,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個(gè)殘疾皇子捣域,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,465評論 2 348

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