回顧目錄
- 業務場景 | 同步秒殺實現:Redis在秒殺功能的實踐
- 設計模式 | 一句話理解設計模式
- Apache SkyWalking | SkyWalking之高階玩法
- Apache SkyWalking | SkyWalking Java 外掛貢獻實踐
- 分散式作業排程 | 鏈家分散式作業平臺(作者推薦)
引言
《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操作的攔截,需要學習對應客戶端的原始碼。
設計外掛
理解外掛實現過程,找到最佳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;