skywalking之trace、tracesegment和span以及ContextCarrier和ContextSnapshot


一、SkyWalking是一款 分布式追蹤系統,隨着微服務架構的流行,一些微服務架構下的問題也會越來越突出,比如一個請求會涉及多個服務,而服務本身可能也會依賴其他服務,整個請求路徑就構成了一個網狀的調用鏈,而在整個調用鏈中一旦某個節點發生異常,整個調用鏈的穩定性就會受到影響。skywalking通過使用字節碼增強技術實現對特定方法的監控,並收集數據用於分析。

 

二、名詞解釋

注:關於更詳細的名詞解釋,可以查看官網文檔 https://skyapm.github.io/document-cn-translation-of-skywalking/

1、trace:指的是整個微服務中的調用鏈路,通過traceid實現串聯。

 

2、tracesegment:在openTracing規范中沒有提到tracesegment,這個概念是在Skywalking中新增的,其概念可以理解為一個線程,其類型有跨進程和跨線程。

 
 
3、span:是鏈路中數據收集的最小單位,可以理解為某一個方法。span的分類為EntrySpan、LocalSpan及ExitSpan。
EntrySpan 代表服務提供者, 也是服務器端的端點. 作為一個 APM 系統, 我們的目標是應用服務器. 所以幾乎所有的服務和 MQ-消費者 都是 EntrySpan.
 
LocalSpan 表示普通的 Java 方法, 它與遠程服務無關, 不是 MQ 生產者/消費者, 也不是服務(例如 HTTP 服務)提供者/消費者.
 
ExitSpan 代表一個服務客戶端或 MQ 的生產者, 在 SkyWalking 的早期命名為  LeafSpan. 例如 通過 JDBC 訪問 DB, 讀取 Redis/Memcached 被歸類為 ExitSpan.
 
 
三、上下文載體ContextCarrier

為了實現分布式追蹤, 需要綁定跨進程的追蹤, 並且上下文應該在整個過程中隨之傳播. 這就是 ContextCarrier 的職責.

以下是有關如何在 A -> B 分布式調用中使用 ContextCarrier 的步驟.

  1. 在客戶端, 創建一個新的空的 ContextCarrier.
  2. 通過 ContextManager#createExitSpan 創建一個 ExitSpan 或者使用 ContextManager#inject 來初始化 ContextCarrier.
  3. 將 ContextCarrier 所有信息放到請求頭 (如 HTTP HEAD), 附件(如 Dubbo RPC 框架), 或者消息 (如 Kafka) 中
  4. 通過服務調用, 將 ContextCarrier 傳遞到服務端.
  5. 在服務端, 在對應組件的頭部, 附件或消息中獲取 ContextCarrier 所有內容.
  6. 通過 ContextManager#createEntrySpan 創建 EntrySpan 或者使用 ContextManager#extract 來綁定服務端和客戶端.

讓我們通過 Apache HttpComponent client 插件和 Tomcat 7 服務器插件演示, 步驟如下:

  1. 客戶端 Apache HttpComponent client 插件
       span = ContextManager.createExitSpan("/span/operation/name", contextCarrier, "ip:port");
            CarrierItem next = contextCarrier.items();
            while (next.hasNext()) {
                next = next.next();
                httpRequest.setHeader(next.getHeadKey(), next.getHeadValue());
            }

 

  1. 服務端 Tomcat 7 服務器插件
        ContextCarrier contextCarrier = new ContextCarrier();
            CarrierItem next = contextCarrier.items();
            while (next.hasNext()) {
                next = next.next();
                next.setHeadValue(request.getHeader(next.getHeadKey()));
            }

            span = ContextManager.createEntrySpan(“/span/operation/name”, contextCarrier);

 

四、上線文快照ContextSnapshot

除了跨進程, 跨線程也是需要支持的, 例如異步線程(內存中的消息隊列)和批處理在 Java 中很常見. 跨進程和跨線程十分相似, 因為都是需要傳播上下文. 唯一的區別是, 跨線程不需要序列化.

以下是有關跨線程傳播的三個步驟:

  1. 使用 ContextManager#capture 方法獲取 ContextSnapshot 對象.
  2. 讓子線程以任何方式, 通過方法參數或由現有參數攜帶來訪問 ContextSnapshot
  3. 在子線程中使用 ContextManager#continued

五、上下文管理器 (ContextManager)

ContextManager 提供所有主要 API.

  1. 創建 EntrySpan
public static AbstractSpan createEntrySpan(String endpointName, ContextCarrier carrier) 

根據操作名稱(例如服務名稱, uri) 和 上下文載體 (ContextCarrier) 創建 EntrySpan.

  1. 創建 LocalSpan
public static AbstractSpan createLocalSpan(String endpointName) 

根據操作名稱(例如完整的方法簽名)創建 (e.g. full method signature)

  1. 創建 ExitSpan
public static AbstractSpan createExitSpan(String endpointName, ContextCarrier carrier, String remotePeer) 

根據操作名稱(例如服務名稱, uri), 上下文載體 (ContextCarrier) 以及對等端 (peer) 地址 (例如 ip + port 或 hostname + port) 創建 ExitSpan.


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM