Skywalking的加強與攔截機制

整理自架構經理(湯哥)的分享

字節加強條件匹配

在 skywalking 中實現不少基於 byte-buddy 的關於鏈式匹配查詢的實現, 代碼以下所示:apache

public abstract class AbstractJunction<V> implements ElementMatcher.Junction<V>

其對應的類的類圖關係以下所示:編程

除此以外, 爲了便於更好的執行在攔截時期的的匹配, skywalking  又定義了一套自實現的 Match  的匹配器, 類的簡要類圖關係以下所示:架構

 

其中 NameMatch 名稱 Match 的 NameMatch 好象相對類的繼承相關較獨立,可是其它方式的 Match 繼承結構相對有點複雜, 都繼承於IndirectMatch , 上面類的簡意類圖對應的類的完整類圖詳情以下所示:框架

插件方式加強的擴展點

全部的基於 skywalking 的整套加強邏輯框架下,如查有新的要加強的中間件或組件, 必須遵照其制定的插件擴展規範, 其具體的規範以下:ui

1、 在對應的插件的 jar 包的 classpath 下必須存在定義文件 skywalking-plugin.def ,文件中必須包括 對 JTI 中的 Instrumentation 的自定義類的全限定名, 以 Dubbo 加強爲例, 其內容爲

dubbo=org.apache.skywalking.apm.plugin.dubbo.DubboInstrumentation

2、自定義的 Instrumentation 必須繼承至 ClassInstanceMethodsEnhancePluginDefine 抽象類 , 在類中,須指定這個設施的加強的兩個核心要素內容:

A). 需要加強的目標類 , Enhance Class
B). 插件中本身要實現的具體的攔截實現 xxxxxInterceptor
一樣以 Dubbo 爲例, 若是要自定義本身的插件實的話,則就要指定以下內容:spa

3、最後就是要真實的定義一個 實現接口 InstanceMethodsAroundInterceptor  的一個攔截實現

對於每個插件來講, 不必定只能有一個 Interceptor , 還能夠有多個, 由於有時不必定只是要對一個類或一個方法進行攔截, 多是多個類, 也有多是一個類中的多個方法, 或多個類的多個方法,所以,在最初的 Instrumentation 入口定義以外,就提供了能夠多個方法的加強擴展, 具體的以下圖針對 Dubbo 的加強所示:插件

插件方式加強主體邏輯

理解實現插件加強機理的類與類之間的關係,猶爲重要,它也是 sky-walking 的對於中間件以插件方式進行加強操做的核心, 下面咱們就來仔細的分析下,是如何進行自定義插件, 並如何進行對中間件的加強的。在整個的加強的機制中, ClassEnhancePluginDefine 是很重要, 很核心的一個類, 在其 enhance 方法中, 分別指定了對3d

  • 靜態方法加強
  • 實現方法加強
  • 定義了 ConstructorInterceptPoint、InstanceMethodsInterceptPoint、StaticMethodsInterceptPoint

這三個 構造器,實例方法, 靜態方法攔截點的 抽象 方法, 讓插件子類去實現攔截點code

 下面是在 Dubbo  爲例 DubboInstrumentation  插件主體實現依賴的類圖關係:orm

上面的是以 ClassEnhancePluginDefine 爲核心的主體的核心類圖結構, 如今主要講一下此類的內部的主要構成與執行序列, 完整的類圖以下所示:

從上面可知, ClassEnhancePluginDefine 繼承至 AbstractClassEnhancePluginDefine , 作爲全部的插件都要繼承實現的基礎, 抽象類AbstractClassEnhancePluginDefine 具體定義了什麼行爲, 這些行爲又有哪些意義?什麼地方在調用這個方法?

AbstractClassEnhancePluginDefine 中有四個方法

  • enhance 抽象方法, 抽象類中無實現, 子類去實現,具體的加強的邏輯
  • enhanceClass 抽象方法, 抽象類中無實現,子類去實現,具體的要加強的類的 Match
  • witnessClasses 也是能夠被重載的一個方法,子類可重載
  • define 實例方法,它主要作了下面的幾回事 ( 入口方法)

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  方法必須所具有的操做。此方法的內部主要作了兩件事:

  • enhanceClass , 加強一個類,以攔截其靜態方法
  • enhanceInstance ,加強一個類,以攔截其構造器 與 實例方法

除了重載的方法外, 其它的三個方法, 分別提供了對應的攔截點:

 在實現自定義插件邏輯的時候, 要重載這三個方法中的實現, 以 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 方能正常的執行加強工做。

由於這三個固定類型的攔截器的處理方式都是差很少, 這裏就以 靜態方法攔截器舉例分析其內部的執行原理。先看一下這個攔截器的類
的依賴狀況:

從上面的此攔截器的依賴狀況能夠, 它主要依賴兩個:

  • StaticMethodsAroundInterceptor , 這個接口的實現者是自定義插件自行根據需求實現的
  • InterceptorInstanceLoader ,這個是用戶自定義插件的攔截器的類加載器

以前咱們說, 執行 byte-buddy 固定的 intercept 邏輯範式時, 經過 MethodDelegation 委託給了 sky-walking 的預設定的幾個類型的攔截器,在構建這些固定攔截器時, 傳入的都是用戶自定義的攔截器的 ClassFullName , 因此在真實的固定類型的攔截器內部,就得有一個機制去加載用戶自定義的攔截器,只有這樣, 這些攔截器才能被調用執行。
這些攔截器有三個固定的方法:

  • beforeMethod , 被攔截方法以前執行
  • afterMethod ,被攔截方法以後執行
  • handleMethodException ,處理捕獲執行期間的異常

OK , 分析到這裏, 基本上, 用戶自定義加強插件,如何被加強,如何被攔截執行的過程應該算是比較清楚了。

加強與攔截機制總結

下圖是整理後的關於 sky-walking  的 APM 中用戶自定義插件的完整的調用鏈路, 它對於插件如何生效並進行加強與攔截的調用過程作了描繪。

相關文章
相關標籤/搜索