SkyWalking Java 插件貢獻實踐

引言

《SkyWalking Java 插件貢獻實踐》:本文將基於SkyWalking 6.0.0-GA-SNAPSHOT版本,以編寫Redis客戶端Lettuce的SkyWalking Java Agent 插件爲例,與你們分享我貢獻PR的過程,但願對你們瞭解SkyWalking Java Agent插件有所幫助。java

基礎概念

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

  • Span:可理解爲一次方法調用,一個程序塊的調用,或一次RPC/數據庫訪問。只要是一個具備完整時間週期的程序訪問,均可以被認爲是一個span。SkyWalking Span對象中的重要屬性github

    屬性 名稱 備註
    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組成。redis

  • Trace segment:Segment是SkyWalking中的一個概念,它應該包括單個OS進程中每一個請求的全部範圍,一般是基於語言的單線程。由多個歸屬於本線程操做的Span組成。docker

核心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操做的攔截,須要學習對應客戶端的源碼。apache

設計插件

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;

相關文章
相關標籤/搜索