Dubbo過濾器概述
Dubbo中的過濾器和Web應用中的過濾器的概念是一樣的,提供了在服務調用前後插入自定義邏輯的途徑。過濾器是整個Dubbo框架中非常重要的組成部分,Dubbo中很多功能都是基於過濾器擴展而來的。過濾器提供了服務提供者和消費者調用過程的攔截,即每次都執行RPC調用的時候,對應的過濾器都會生效。雖然過濾器的功能強大,但由於每次調用時都會執行,因此在使用的時候需要注意它對性能的影響。
過濾器的使用
- 一種方式是使用@Active註解默認啓用;
- 一種方式是在配置文件中配置;
<!-- 消費方調用過程攔截 -->
<dubbo:reference filter="xxx, yyy" />
<!-- 消費方調用過程默認攔截器,將攔截所有reference -->
<dubbo:consumer filter="xxx, yyy" />
<!-- 服務提供方調用過程攔截 -->
<dubbo:service filter="xxx, yyy" />
<!-- 服務提供方調用過程默認攔截器,將攔截所有service -->
<dubbo:provider filter="xxx, yyy" />
配置上的“潛規則”:
-
過濾器順序:
- 用戶自定義的過濾器的順序默認會在框架內置過濾器之後,可以使用
filter="xxx, default"
這種配置方式讓自定義的過濾器順序靠前。 - 我們在配置
filter="xxx, yyy"
時,寫在前面的xxx會比yyy的順序要靠前。
- 用戶自定義的過濾器的順序默認會在框架內置過濾器之後,可以使用
-
剔除過濾器。對於一些默認的過濾器或自動激活的過濾器,有些方法不想使用這些過濾器,可以使用
"-"
加過濾器名稱來過濾,如filter="-xxFilter"
會讓xxFilter不生效。如果不想使用所有默認啓用的過濾器,則可以配置filter="-default"
來進行剔除。 -
過濾器的疊加。如果服務提供者、消費者端都配置了過濾器,則兩邊的過濾器不會相互覆蓋,而是互相疊加,都會生效。如果需要覆蓋,則可以在消費方使用
"-"
的方式剔除對應的過濾器。
過濾器的總體結構
所有的內置過濾器中除了CompatibleFilter特別突出,只繼承了Filter接口,既不會被默認激活,其他的內置過濾器都使用了Activate註解,即默認被激活。Filter接口上有@SPI註解,說明過濾器是一個擴展點,用戶可以基於這個擴展點接口實現自己的過濾器。
所有的過濾器會被分爲消費者和服務提供者兩種類型,消費者類型的過濾器只會在服務引用時被加入Inoker,服務提供者類型的過濾器只會在服務暴露的時候被加入對應的Invoker。MonitorFilter比較特殊,它會同時在暴露和引用被加入Invoker。
過濾器名 | 作用 | 使用方 |
---|---|---|
AccessLogFilter | 打印每一次請求的訪問日誌。如果需要訪問的日誌只出現在指定的appender中,則可以在log的配置文件中配置additivity | 服務提供者 |
ActiveLimitFilter | 用於限制消費者端對服務的最大並行調用數 | 消費者 |
ExecuteLimitFilter | 同上,用於限制服務端的最大並行調用數」服務提供者 | |
ClassLoaderFilter | 用於切換不同線程的類加載器,服務調用完成後會還原回去 | 服務提供者 |
CompatibleFilter | 用戶使返回值與調用程序的對象版本兼容,默認不啓用。如果啓用,則會把JSON或fastjson類型的返回值轉換爲Map類型;如果返回類型和本地接口中定義的不同,則會做POJO的轉換 | - |
ConsumerContextFilter | 爲消費者把一些上下文信息設置到當前線程的RpcContext對象中,包括invocation、localhost、remote host等 | 消費者 |
ContextFilter | 同上,但是爲服務提供者服務 | 服務提供者 |
DeprecatedFilter | 如果調用的方法被標記位已棄用,那麼DeprecatedFilter將記錄一個錯誤消息 | 消費者 |
EchoFilter | 用於echo測試 | 服務提供者 |
ExceptionFilter | 用於統一的異常處理,防止出現序列化失敗 | 服務提供者 |
GenericFilter | 用於服務提供者端,實現泛化調用,實現序列化的檢查和處理 | 服務提供者 |
GenericImplFilter | 同上,但用於消費者端 | 消費者 |
TimeoutFilter | 如果某些服務調用超時,則自動記錄告警日誌 | 服務提供者 |
TokenFilter | 服務提供者下令發牌給消費者,通常用於防止消費者繞過註冊中心直接調用服務提供者 | 服務提供者 |
TpsLimitFilter | 用於服務端的限流,注意與ExecuteLimitFilter | 消費者 |
FutureFilter | 在發起invoke或得到返回值、出現異常的時候觸發回調事件 | 消費者 |
TraceFilter | Trace指令的使用 | 服務提供者 |
MonitorFilter | 監控並統計所有的接口的調用情況,如成功、失敗、耗時。後續DubboMonitor會定時把該過濾器收集的數據發送到Dubbo-Monitor服務上 | 服務提供者+消費者 |
每個過濾器的使用方不一樣,有的是服務提供者使用,有的是消費者使用。Dubbo是如何保證服務提供者不會使用消費者的過濾器的呢?答案就在@Activate註解上,該註解可以設置過濾器激活的條件和順序,如@Activate(group = Constants.PROVIDER, order = -110000)
表示在服務提供端擴展點實現纔有效,並且過濾器的順序是 -110000.
過濾器初始化的實現原理
服務的暴露與引用會使用Protocol層,而ProtocolFilterWrapper包裝類則實現了過濾器鏈的組裝。在服務的暴露與引用過程中,會使用ProtocolFilterWrapper#buildInvokerChain
方法組裝整個過濾器鏈:
buildInvokerChain方法構造調用鏈的步驟:
- 獲取並遍歷所有過濾器。通過
ExtensionLoader#getActivateExtension
方法獲取所有的過濾器並遍歷。 - 使用裝飾器模式,增強原有Invoker,組裝過濾器鏈。使用裝飾器模式,像俄羅斯套娃一樣,把過濾器一個又一個套到Invoker上。
爲什麼要倒排遍歷呢?
因爲是通過從裏到外構造匿名類的方式構造Invoekr的,所以只有倒排,最外層的Invoker才能是第一個過濾器。