整理自架構經理(湯哥)的分享
在 skywalking 中實現不少基於 byte-buddy 的關於鏈式匹配查詢的實現, 代碼以下所示:apache
public abstract class AbstractJunction<V> implements ElementMatcher.Junction<V>
其對應的類的類圖關係以下所示:編程
除此以外, 爲了便於更好的執行在攔截時期的的匹配, skywalking 又定義了一套自實現的 Match 的匹配器, 類的簡要類圖關係以下所示:架構
其中 NameMatch 名稱 Match 的 NameMatch 好象相對類的繼承相關較獨立,可是其它方式的 Match 繼承結構相對有點複雜, 都繼承於IndirectMatch , 上面類的簡意類圖對應的類的完整類圖詳情以下所示:框架
全部的基於 skywalking 的整套加強邏輯框架下,如查有新的要加強的中間件或組件, 必須遵照其制定的插件擴展規範, 其具體的規範以下:ui
dubbo=org.apache.skywalking.apm.plugin.dubbo.DubboInstrumentation
A). 需要加強的目標類 , Enhance Class
B). 插件中本身要實現的具體的攔截實現 xxxxxInterceptor
一樣以 Dubbo 爲例, 若是要自定義本身的插件實的話,則就要指定以下內容:spa
對於每個插件來講, 不必定只能有一個 Interceptor , 還能夠有多個, 由於有時不必定只是要對一個類或一個方法進行攔截, 多是多個類, 也有多是一個類中的多個方法, 或多個類的多個方法,所以,在最初的 Instrumentation 入口定義以外,就提供了能夠多個方法的加強擴展, 具體的以下圖針對 Dubbo 的加強所示:插件
理解實現插件加強機理的類與類之間的關係,猶爲重要,它也是 sky-walking 的對於中間件以插件方式進行加強操做的核心, 下面咱們就來仔細的分析下,是如何進行自定義插件, 並如何進行對中間件的加強的。在整個的加強的機制中, ClassEnhancePluginDefine 是很重要, 很核心的一個類, 在其 enhance 方法中, 分別指定了對3d
這三個 構造器,實例方法, 靜態方法攔截點的 抽象 方法, 讓插件子類去實現攔截點code
下面是在 Dubbo 爲例 DubboInstrumentation 插件主體實現依賴的類圖關係:orm
上面的是以 ClassEnhancePluginDefine 爲核心的主體的核心類圖結構, 如今主要講一下此類的內部的主要構成與執行序列, 完整的類圖以下所示:
從上面可知, ClassEnhancePluginDefine 繼承至 AbstractClassEnhancePluginDefine , 作爲全部的插件都要繼承實現的基礎, 抽象類AbstractClassEnhancePluginDefine 具體定義了什麼行爲, 這些行爲又有哪些意義?什麼地方在調用這個方法?
AbstractClassEnhancePluginDefine 中有四個方法
1 找到加強插件類的全部顯式指定的 WitnessClasses - 用戶本身重載顯式指定
2 調用 enhance 方法, 執行真正的插件的加強邏輯,返回新的 DynamicType.Builder
3 設置上下文, 標記初始化定義步驟已完成
4 最後再返回新的 第 2 中的加強後的 newClassBuilder
那麼在這個類中,最爲重要的一個方法 define 又是誰調用的呢?
上面的就是這個類方法被使用的 Usages 狀況, 主要是被 Sky-Walking Agent 的 Agent 主入口調用, 這個方法也是使用 byte-buddy 的字節碼加強的 agent 中的一個使用範式, 也就是說在 agent 執行真正的 transformer , 全部的插件中由於都是繼承至AbstractClassEnhancePluginDefine , 天然它們的 define 的定義的初始化方法, 也就會被所有調用初始化, 代碼以下所示:
對於 snow-walking 來講, 使用 Agent 的固定範式( 來自於 byte-buddy 的固定實現方式)以下所示:
至此,已經分析完了, 全部 sky-walking 的自定義插件都必需要繼承並重載的抽象類 AbstractClassEnhancePluginDefine , 它定義了全部的Agent 插件都必需要實現的行爲, 以及它自身須要定義與初始化的行爲邏輯。
接下爲, 真正的主角上場了, 它就是 ClassEnhancePluginDefine , 其類的依賴關係以下:
下面看一下 類的自己的方法, 總共有 六 個方法:
enhance 方法, 重載了父類抽象類 AbstractClassEnhancePluginDefine 的方法, 作爲全部插件的基類, 它定義了 enhance 方法必須所具有的操做。此方法的內部主要作了兩件事:
除了重載的方法外, 其它的三個方法, 分別提供了對應的攔截點:
在實現自定義插件邏輯的時候, 要重載這三個方法中的實現, 以 Dubbo 插件攔截擴展爲例, 在 DubboInstrumentation 中 就重載了 父類的getInstanceMethodsInterceptPoints方法, 以此來告訴父類中的共性 enhance 加強處理邏輯, 本插件的實例方法攔截點的一些元數據信, 區配的方法是什麼,要加強的類是哪個,是否要重載其方法參數,這些信息。
上面這裏, 在具體的 ClassEnhancePluginDefine 實現插件內部, 對於攔截器 intercepter 都是指定的是 字符串 String 類型, 那麼父類中ClassEnhancePluginDefine 是如何轉化,並在什麼時機調用這個 intercepter 的實現類, 以執行攔截的真實的邏輯的呢?
那上面的這裏從用戶自定義的插件中顯式的指定了 intercepter Fullname了,到了ClassEnhancePluginDefine後,進行enhance時,會最終都落在 byte-buddy 的 builder. method (....).intercpet (MethodDelegation.xxxx)
這樣的固定加強的編程範式當中去。在 sky-walking 的實現中, 它又將 intercepter 的名稱, 根據不一樣的加強類型,傳入了不一樣類型的具體的委託執行實例當中去執行了。在 sky-walking 中, 根據加強的分類類型,委託執行實例分爲如下幾種:
它們分別是, 靜態方法固定攔截委託實現器,實例方法固定攔截委託實現器,構造器固定攔截委託實現器, 它們只有一個主要的核心執行方法, 就是 intercept , 這個方法在執行時是與 byte-budy 的 MethodDelegation 配套使用的, 此方法必需要顯式的按規定指定相應的byte-buddy 的 Annotation 方能正常的執行加強工做。
由於這三個固定類型的攔截器的處理方式都是差很少, 這裏就以 靜態方法攔截器舉例分析其內部的執行原理。先看一下這個攔截器的類
的依賴狀況:
從上面的此攔截器的依賴狀況能夠, 它主要依賴兩個:
以前咱們說, 執行 byte-buddy 固定的 intercept 邏輯範式時, 經過 MethodDelegation 委託給了 sky-walking 的預設定的幾個類型的攔截器,在構建這些固定攔截器時, 傳入的都是用戶自定義的攔截器的 ClassFullName , 因此在真實的固定類型的攔截器內部,就得有一個機制去加載用戶自定義的攔截器,只有這樣, 這些攔截器才能被調用執行。
這些攔截器有三個固定的方法:
OK , 分析到這裏, 基本上, 用戶自定義加強插件,如何被加強,如何被攔截執行的過程應該算是比較清楚了。
下圖是整理後的關於 sky-walking 的 APM 中用戶自定義插件的完整的調用鏈路, 它對於插件如何生效並進行加強與攔截的調用過程作了描繪。