以編寫Redis客戶端Agent 插件為例SkyWalking之Java 插件貢獻實踐

基礎概念

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。

插件實踐

Lettuce操作redis代碼

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

/<string>/<string>/<string>/<string>

插件源碼架構

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

設計插件

以編寫Redis客戶端Agent 插件為例SkyWalking之Java 插件貢獻實踐

理解插件實現過程,找到最佳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是一個跨編程語言的標準

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

Redisson是一個非常易用Java的Redis客戶端, 它沒有學習曲線,無需知道任何Redis命令即可開始使用它。

關注我:私信回覆“555”獲取往期Java高級架構資料、源碼、筆記、視頻Dubbo、Redis、Netty、zookeeper、Spring cloud、分佈式、高併發等架構技術往期架構視頻截圖


分享到:


相關文章: