10予权、Skywalking的埋點-插件開發(fā)的基本技巧

1. 概述

做中間件的埋點插件,需要通過對源碼梳理浪册,找到關(guān)鍵攔截點扫腺,如什么類的什么方法,通過其參數(shù)村象、返回值笆环、對象屬性等獲取構(gòu)建Span的數(shù)據(jù)信息。這節(jié)內(nèi)容記錄一下開發(fā)插件過程中一些零碎的事項厚者。
通過繼承ClassInstanceMethodsEnhancePluginDefine躁劣,在子類的實現(xiàn)方法中完成以下邏輯:

  1. 指定待增強的類
    @Override
    protected ClassMatch enhanceClass() {
        IndirectMatch hierarchyMatch = byHierarchyMatch(new String[]{ENHANCE_CLASS});
        PrefixMatch prefixMatch = nameStartsWith(PAJK_PACKAGE_PREFIX);
        return LogicalMatchOperation.and(hierarchyMatch, prefixMatch);
    }
  1. 在構(gòu)造函數(shù)中添加增強邏輯,可通過類名(構(gòu)造方法同名)籍救,方法參數(shù)信息指定構(gòu)造函數(shù)习绢。
    //這種方式,不做構(gòu)造函數(shù)增強
    @Override
    public ConstructorInterceptPoint[] getConstructorsInterceptPoints() {
        return new ConstructorInterceptPoint[0];
    }

    //指定構(gòu)造方法
    @Override
    public ConstructorInterceptPoint[] getConstructorsInterceptPoints() {
        return new ConstructorInterceptPoint[]{
                new ConstructorInterceptPoint() {
                    @Override
                    public ElementMatcher<MethodDescription> getConstructorMatcher() {
                        //第二個參數(shù)必須是String
                        return takesArgumentWithType(2, "java.lang.String");
                    }

                    @Override
                    public String getConstructorInterceptor() {
                        return INTERCEPT_CLASS;//指定類名(構(gòu)造方法名)
                    }
                }
        };
    }
  1. 增強實例方法,通過InstanceMethodsInterceptPoint匹配將增強的方法
    @Override
    public InstanceMethodsInterceptPoint[] getInstanceMethodsInterceptPoints() {
        return new InstanceMethodsInterceptPoint[]{
                new InstanceMethodsInterceptPoint() {
                    @Override
                    public ElementMatcher<MethodDescription> getMethodsMatcher() {
                        return named(ENHANCE_METHOD);
                    }

                    @Override
                    public String getMethodsInterceptor() {
                        return INTERCEPTOR_CLASS;
                    }

                    @Override
                    public boolean isOverrideArgs() {
                        return false;
                    }
                }
        };
    }
  1. 增強類(靜態(tài))方法闪萄,通過StaticMethodsInterceptPoint匹配將增強的類方法
    @Override
    public StaticMethodsInterceptPoint[] getStaticMethodsInterceptPoints() {
        return new StaticMethodsInterceptPoint[0];
    }

2. xxxMatch匹配的技巧

在以上代碼中可以看到許多xxxMatch梧却,大致是2類:

  • ClassMatch :用于匹配類
  • ElementMatcher :用于匹配方法
2.1 ClassMatch

在源碼中可看到ClassMatch有如下這些實現(xiàn)類:

ClassAnnotationMatch
EitherInterfaceMatch
FailedCallbackMatch
HierarchyMatch
IndirectMatch
ListenableFutureCallbackMatch
LogicalAndMatch
LogicalOrMatch
MethodAnnotationMatch
MultiClassNameMatch
NameMatch
PrefixMatch
RegexMatch
SuccessCallbackMatch

這些匹配有按照類名字匹配、前綴匹配败去、繼承關(guān)系放航、實現(xiàn)接口、組合與匹配圆裕、組合或匹配等等广鳍;可通過在源碼中查找其應(yīng)用來進一步理解其作用;另外需要注意吓妆,除了默認(rèn)提供的這些赊时,還是可以根據(jù)自己的需求來額外定制(通過實現(xiàn)接口IndirectMatch

2.2 ElementMatcher
  1. 系統(tǒng)api
    ElementMatchers中已經(jīng)定義了很多用于方法匹配的方法,因其方法太多行拢,不在此列舉祖秒,自行查看

  2. 自定義matcher

return new ElementMatcher<MethodDescription>() {
    @Override
    public boolean matches(MethodDescription target) {
       //自己編排邏輯
       if(xxx) { 
            return true;
       }
       return false;
   }

3. 上下文數(shù)據(jù)的傳遞

插件埋點所需要的信息,通常是需要再多個方法中分別捕獲舟奠,我們需要通過某種上下文機制將這些分散的信息搜集整合起來竭缝;從線程角度說通常分為同步請求(相同線程內(nèi)完成一次執(zhí)行)和異步請求(不同的線程內(nèi)完成一次請求)。

3.1 同步請求的上下文數(shù)據(jù)
  1. ThreadLocal方式沼瘫,在Skywalking中已為我們提供了這種途徑ContextManager.getRuntimeContext()抬纸;
  • 存儲數(shù)據(jù)
    ContextManager.getRuntimeContext().put(key, data)
  • 檢查數(shù)據(jù)
    ContextManager.getRuntimeContext().get(key),判斷是否存在
  • 清理數(shù)據(jù)
    需要留意做善后清理數(shù)據(jù)ContextManager.getRuntimeContext().remove(key)
  1. Skywalking的擴展字段,Skywalking會在被增強的類中添加一個sw專用的屬性,同時這個類會被修改耿戚,實現(xiàn)了接口EnhancedInstance湿故,通過此接口中的2個方法來讀寫這個擴展屬性
public interface EnhancedInstance {
    Object getSkyWalkingDynamicField();

    void setSkyWalkingDynamicField(Object value);
}

這個sw專用字段,就是一個普通的Object溅话,可根據(jù)自己的需求給其賦值晓锻。
如這樣一些使用場景:

  • 攔截目標(biāo)類的構(gòu)造方法,在構(gòu)造房中new 一個自定義類飞几,通過setSkyWalkingDynamicField賦值給擴展字段
  • 在其他方法中砚哆,捕獲不同的數(shù)據(jù),填充到這個對象的擴展屬性里屑墨。
  • 讀取擴展屬性中的數(shù)據(jù)躁锁,構(gòu)造、填充Span
3.2 異步請求的上下文數(shù)據(jù)

異步請求的場景下卵史,因為跨越了線程战转,所以上下文需要在多個線程中傳遞,那么常規(guī)的ThreadLocal方式就不可用了以躯;通過SW擴展屬性來承載數(shù)據(jù)槐秧,在多個線程中傳遞的方式非常方便了啄踊。

異步請求時 需要借助AsyncSpan#prepareForAsyncAsyncSpan#asyncFinish來完成span的異步關(guān)閉,這里一個問題刁标,異步跨線程的情況下颠通,另外的線程里如何拿到這個span實例呢? 尋找在多個線程中都存在的對象膀懈,如果這個對象本身有承載額外數(shù)據(jù)的能力最好顿锰,如果沒有,則需要增強這個類启搂,借助sw的擴展字段來承載這個span硼控,進而在異步結(jié)果處理中完成span的關(guān)閉。

異步請求的上下文的實例胳赌,通過在源碼中搜索prepareForAsync方法的使用來加深理解,這里用ExitSpan方式暫記一下關(guān)鍵邏輯和步驟:

  1. 發(fā)送請求的線程a牢撼,創(chuàng)建ExitSpan 實例 exitSpan,會對exitSpan做一些賦值
  2. 線程b中會獲取當(dāng)前請求的執(zhí)行結(jié)果匈织。
  3. 線程a 和線程b 之間一定會有一個對象浪默,在兩個線程之間都可訪問牡直,暫叫 externObj;
  4. 線程a缀匕,在發(fā)起異步請求之前,調(diào)用exitSpan的prepareForAsync方法碰逸,并把exitSpan裝入externObj中乡小;如果externObj沒有合適的屬性去承載exitSpan數(shù)據(jù),就擴展這個externObj的類饵史,比如使用skywalking的類增強或者繼承externObj的類满钟,達到增加屬性字段的效果
  5. 線程b,在拿到響應(yīng)結(jié)果后胳喷,從externObj的取出exitSpan湃番,根據(jù)返回值 給 exitSpan做賦值,最后調(diào)用asyncFinish異步關(guān)閉span

4. SpanStack

1. EntrySpan 的SpanStack

服務(wù)接收請求時吭露,創(chuàng)建EntrySpan;tomcat和SpringMVC的插件都是創(chuàng)建 EntrySpan吠撮,這樣就重復(fù)創(chuàng)建了EntySpan,這樣沒有意義,在sw中讲竿,只要最外層的EntrySpan,通過一個計數(shù)器(stackDepth)和棧的結(jié)構(gòu)來實現(xiàn)泥兰,大致流程如下:

  • 請求進入1處,創(chuàng)建EntrySpan 暫叫tomEntrySpan题禀,stackDepth=1,記錄開始時間
  • 請求進入2處鞋诗,創(chuàng)建EntrySpan時,stackDepth+1=2迈嘹,會復(fù)用上一步創(chuàng)建的tomEntrySpan對象削彬,而不是new 一個新的;同時會清理此span上的layer、logs和tags融痛;保留springMVC層的layer糕篇、logs和tags。
  • 請求進入3處酌心,執(zhí)行finish方法將stackDepth-- = 1;
  • 請求進入4處拌消,執(zhí)行finish方法將stackDepth--=0,關(guān)閉tomEntrySpan安券,記錄結(jié)束時間
image.png
2. ExitSpan 的SpanStack

假設(shè)上圖tomcat是作為調(diào)用外部請求的墩崩,創(chuàng)建ExitSpan(這里只是借圖舉例)其邏輯流程卻是這樣:

  • 請求進入1處,創(chuàng)建ExitSpan實例 tomExitSpan侯勉,stackDepth=1鹦筹,記錄開始時間;在這一層才可以記錄layer、logs和tags址貌。
  • 請求進入2處铐拐,創(chuàng)建ExitSpan時,stackDepth+1=2练对,會復(fù)用上一步創(chuàng)建的tomExitSpan對象遍蟋,而不是new 一個新的;這里layer螟凭、logs和tags設(shè)置無效虚青。
  • 請求進入3處,執(zhí)行finish方法將stackDepth-- = 1;
  • 請求進入4處螺男,執(zhí)行finish方法將stackDepth--=0棒厘,關(guān)閉tomExitSpan,記錄結(jié)束時間。
3. 方法的重入

比如在ExitSpan場景下中下隧,A類的put1和put2方法都被攔截奢人,并且put1方法 調(diào)用了put2方法,在上述的SpanStack的機制里淆院,由于put2方法中stackDepth=2何乎,其內(nèi)所處理的layer、logs和tags是無效的迫筑;對于這種情況宪赶,大概有2中方法處理:

  1. 當(dāng)stackDepth!=1 的時候脯燃,通過上下文記錄layerlayer搂妻、logs和tags的信息,等stackDepth=1時辕棚,從上下文中取出layerlayer欲主、logs和tags的信息賦值給span邓厕。
  2. 控制span的創(chuàng)建時機,自行在上下文中增加stackDepth的計數(shù)控制扁瓢,當(dāng)stackDepth>1時详恼,不在創(chuàng)建ExitSpan。即put1中有一個ExitSpan引几,put2中不創(chuàng)建ExitSpan昧互,那么stackDepth一直=1,layerlayer伟桅、logs和tags的信息可以隨時賦值給span
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末敞掘,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子楣铁,更是在濱河造成了極大的恐慌玖雁,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,607評論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件盖腕,死亡現(xiàn)場離奇詭異赫冬,居然都是意外死亡,警方通過查閱死者的電腦和手機溃列,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,239評論 3 395
  • 文/潘曉璐 我一進店門劲厌,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人哭廉,你說我怎么就攤上這事脊僚。” “怎么了遵绰?”我有些...
    開封第一講書人閱讀 164,960評論 0 355
  • 文/不壞的土叔 我叫張陵,是天一觀的道長增淹。 經(jīng)常有香客問我椿访,道長,這世上最難降的妖魔是什么虑润? 我笑而不...
    開封第一講書人閱讀 58,750評論 1 294
  • 正文 為了忘掉前任成玫,我火速辦了婚禮,結(jié)果婚禮上拳喻,老公的妹妹穿的比我還像新娘哭当。我一直安慰自己,他們只是感情好冗澈,可當(dāng)我...
    茶點故事閱讀 67,764評論 6 392
  • 文/花漫 我一把揭開白布钦勘。 她就那樣靜靜地躺著,像睡著了一般亚亲。 火紅的嫁衣襯著肌膚如雪彻采。 梳的紋絲不亂的頭發(fā)上腐缤,一...
    開封第一講書人閱讀 51,604評論 1 305
  • 那天,我揣著相機與錄音肛响,去河邊找鬼岭粤。 笑死,一個胖子當(dāng)著我的面吹牛特笋,可吹牛的內(nèi)容都是我干的剃浇。 我是一名探鬼主播,決...
    沈念sama閱讀 40,347評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼猎物,長吁一口氣:“原來是場噩夢啊……” “哼偿渡!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起霸奕,我...
    開封第一講書人閱讀 39,253評論 0 276
  • 序言:老撾萬榮一對情侶失蹤溜宽,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后质帅,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體适揉,經(jīng)...
    沈念sama閱讀 45,702評論 1 315
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,893評論 3 336
  • 正文 我和宋清朗相戀三年煤惩,在試婚紗的時候發(fā)現(xiàn)自己被綠了嫉嘀。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 40,015評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡魄揉,死狀恐怖剪侮,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情洛退,我是刑警寧澤瓣俯,帶...
    沈念sama閱讀 35,734評論 5 346
  • 正文 年R本政府宣布,位于F島的核電站兵怯,受9級特大地震影響彩匕,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜媒区,卻給世界環(huán)境...
    茶點故事閱讀 41,352評論 3 330
  • 文/蒙蒙 一驼仪、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧袜漩,春花似錦绪爸、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,934評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至粘优,卻和暖如春仇味,著一層夾襖步出監(jiān)牢的瞬間呻顽,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,052評論 1 270
  • 我被黑心中介騙來泰國打工丹墨, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留廊遍,地道東北人。 一個月前我還...
    沈念sama閱讀 48,216評論 3 371
  • 正文 我出身青樓贩挣,卻偏偏與公主長得像喉前,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子王财,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,969評論 2 355

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