SkyWalking Java 外掛貢獻實踐

於玉桔發表於2019-01-21

回顧目錄

引言

《SkyWalking Java 外掛貢獻實踐》:本文將基於SkyWalking 6.0.0-GA-SNAPSHOT版本,以編寫Redis客戶端Lettuce的SkyWalking Java Agent 外掛為例,與大家分享我貢獻PR的過程,希望對大家瞭解SkyWalking Java Agent外掛有所幫助。

基礎概念

OpenTracing和SkyWalking鏈路模組幾個很重要的語義概念。

  • Span:可理解為一次方法呼叫,一個程式塊的呼叫,或一次RPC/資料庫訪問。只要是一個具有完整時間週期的程式訪問,都可以被認為是一個span。SkyWalking Span物件中的重要屬性

    屬性 名稱 備註
    component 元件 外掛的元件名稱,如:Lettuce,詳見:ComponentsDefine.Class。
    tag 標籤 k-v結構,關鍵標籤,key詳見:Tags.Class。
    peer 對端資源 用於拓撲圖,若DB元件,需記錄叢集資訊。
    operationName 操作名稱 若span=0,operationName將會搜尋的下拉選單。
    layer 顯示 在鏈路頁顯示,詳見SpanLayer.Class。
  • Trace:呼叫鏈,通過歸屬於其的Span來隱性的定義。一條Trace可被認為是一個由多個Span組成的有向無環圖(DAG圖),在SkyWalking鏈路模組你可以看到,Trace又由多個歸屬於其的trace segment組成。

  • Trace segment:Segment是SkyWalking中的一個概念,它應該包括單個OS程式中每個請求的所有範圍,通常是基於語言的單執行緒。由多個歸屬於本執行緒操作的Span組成。

核心API

跨程式ContextCarrier核心API

  • 為了實現分散式跟蹤,需要繫結跨程式的跟蹤,並且應該傳播上下文 整個過程。 這就是ContextCarrier的職責。
  • 以下是實現有關跨程式傳播的步驟:
    • 在客戶端,建立一個新的空的ContextCarrier,將ContextCarrier所有資訊放到HTTP heads、Dubbo attachments 或者Kafka messages。
    • 通過服務呼叫,將ContextCarrier傳遞到服務端。
    • 在服務端,在對應元件的heads、attachments或messages獲取ContextCarrier所有訊息。將服務端和客戶端的鏈路資訊繫結。

跨執行緒ContextSnapshot核心API

  • 除了跨程式,跨執行緒也是需要支援的,例如非同步執行緒(記憶體中的訊息佇列)和批處理在Java中很常見,跨程式和跨執行緒十分相似,因為都是需要傳播 上下文。 唯一的區別是,不需要跨執行緒序列化。
  • 以下是實現有關跨執行緒傳播的步驟:
    • 使用ContextManager#capture獲取ContextSnapshot物件。
    • 讓子執行緒以任何方式,通過方法引數或由現有引數攜帶來訪問ContextSnapshot。
    • 在子執行緒中使用ContextManager#continued。

詳盡的核心API相關知識,可點選閱讀 《外掛開發指南-中文版本

外掛實踐

Lettuce操作redis程式碼

@PostMapping("/ping")
public String ping(HttpServletRequest request) throws ExecutionException, InterruptedException {
    RedisClient redisClient = RedisClient.create("redis://" + "127.0.0.1" + ":6379");
    StatefulRedisConnection<String, String> connection0 = redisClient.connect();
    RedisAsyncCommands<String, String> asyncCommands0 = connection0.async();
    AsyncCommand<String, String, String> future = (AsyncCommand<String, String, String>)asyncCommands0.set("key_a", "value_a");
    future.onComplete(s -> OkHttpClient.call("http://skywalking.apache.org"));
    future.get();
    connection0.close();
    redisClient.shutdown();
    return "pong";
}
複製程式碼

外掛原始碼架構

Lettuce對Redis封裝與Redisson Redisson 類似,目的均是實現簡單易用,且無學習曲線的Java的Redis客戶端。所以要是先對Redis操作的攔截,需要學習對應客戶端的原始碼。

設計外掛

Lettuce時序圖
理解外掛實現過程,找到最佳InterceptPoint位置是實現外掛融入SkyWalking的核心所在。

程式碼實現

PR的url:Support lettuce plugin

實踐中遇到的問題

  • 多執行緒程式設計使用debug斷點會將鏈路變成同步,建議使用run模式增加log,或者遠端debug來解決。
  • 多執行緒程式設計,需要使用跨執行緒ContextSnapshot核心API,否則鏈路會斷裂。
  • CompleteableCommand.onComplete方法有時會同步執行,這個和內部機制有關,有時候不分離執行緒。
  • 外掛編譯版本若為1.7+,需要將外掛放到可選外掛中。因為sniffer支援的版本是1.6。

外掛相容

為了外掛得到外掛最終的相容相容版本,我們需要使用docker對所有外掛版本的測試,具體步驟如下:

  • 編寫測試用例:關於如何編寫測試用例,請按照如何編寫文件來實現。
  • 提供自動測試用例。 如:Redisson外掛testcase
  • 確保本地幾個流行的外掛版本,在本地執行起來是和自己的預期是一致的。
  • 在提供自動測試用例並在CI中遞交測試後,外掛提交者會批准您的外掛。
  • 最終得到完整的外掛測試報告。

Pull Request

提交PR

提交PR的時候,需要簡述自己對外掛的設計,這樣有助於與社群的貢獻者討論完成codereview。

申請自動化測試

測試用例編寫完成後,可以申請自動化測試,在自己的PR中會生成外掛相容版本的報告。

外掛文件

外掛文件需要更新:Supported-list.md相關外掛資訊的支援。

外掛如果為可選外掛需要在agent-optional-plugins可選外掛文件中增加對應的描述。

註釋

Lettuce是一個完全無阻塞的Redis客戶端,使用netty構建,提供反應,非同步和同步資料訪問。瞭解細節可點選閱讀 lettuce.io;

OpenTracing是一個跨程式語言的標準,瞭解細節可點選閱讀 《OpenTracing語義標準》;

span:org.apache.skywalking.apm.agent.core.context.trace.AbstractSpan介面定義了所有Span實現需要完成的方法;

Redisson是一個非常易用Java的Redis客戶端, 它沒有學習曲線,無需知道任何Redis命令即可開始使用它。瞭解細節可點選閱讀 redisson.org;

相關文章