Trail:分布式追蹤

在又拍云,即使是 應用層服務 也依賴到其他服務,而那些服務又依賴到了更多服務。當一個接口超時時,定位接口的性能瓶頸是困難的。

解決定位服務性能瓶頸和錯誤原因的問題,是實現 Trail:分布式追蹤服務 的初衷。

Trail 做了什么

系統接收到外部的請求后,會在分布式系統內形成復雜的調用關系,

Credit: Dapper, a Large-Scale Distributed Systems Tracing Infrastructure

Trail 采集服務(進程)間的調用,記錄處理調用數據,并提供分析平臺。

工作原理

分布式系統的調用形成樹狀結構,我們稱一次調用形成的調用鏈為 trace ,每個 trace 有唯一 ID traceId ,構成 trace 的最小元素是 span , trace 下的所有 span 有相同的 traceId 。

每個 span 有自己唯一的 ID spanId ,通過在 span 中存儲父 span ID parentId 來建立 span 之間的關系。

Credit: Dapper, a Large-Scale Distributed Systems Tracing Infrastructure

采集

Trail 記錄 span 來記錄服務之間的調用,并生成完整的 trace 。它通過在基礎通信和調用庫中增加采集代碼來實現采集功能。

以 HTTP 協議舉例,在前端 HTTP 服務器進程接收到用戶請求后,會 創建一個新的 span ,這是 trace 中的第一個 span ,該 span 初始化過程中除了生成 spanId 外還會 生成 traceId 。

當前端服務進程 向后端服務進程發送 HTTP 請求 時, 請求頭中被加入了額外信息 ,包括 traceId spanId 。后端進程接收到請求,也會創建 span ,但在創建過程中會直接使用接收到的 traceId ,并設置 parentId 。

span 采集的關鍵是

  1. 客戶端注入參數
  2. 傳輸協議支持并傳遞參數
  3. 服務端解析參數

存儲

Trail 目前沒有實現完善的存儲機制,當 span 采集后,將通過 TCP 請求發送至 Logstash,并被轉存至 Elasticsearch。

處理分析

分析程序通過接口讀取 Elasticsearch 數據,根據需求組裝數據。

如,為了展示一次調用完整的調用鏈,將查詢特定 traceId 下的所有 span ,并通過 parentId 構建調用鏈。

應用場景

調用鏈

服務狀態和關系

在組織服務關系過程中,需要節點 node 和關系 link 兩個原子數據。節點通過對多條 span 聚合標簽獲得,關系通過聚合父子 span 的標簽(有方向性)獲得。在展示服務狀態過程時,數據量較小,節點和關系數據可以在瀏覽器端實時計算。在展示服務關系時,數據量較大,可以通過定時任務在特定時間計算。

性能監控

服務性能數據可以通過 Kibana 和 Elasticsearch 直接生成圖表,

可用性

響應時間

QPS

慢路由

接口請求量

遺留問題

目前 Trail 的采集功能已經完善,然而在存儲,處理和分析功能上還有很多遺留問題。

分布式時鐘同步

在繪制調用鏈的過程中,發現子 span 的開始時間可能小于父 span 時間,這個問題是由不同機器之間存在時間差引起的。因為整個 span 的時間非常短(通常只有十幾毫秒),機器間細微的時間不同步也會導致這種現象。該問題仍未解決,僅在繪制調用鏈的過程中補償時間差。

異步調用

Trail 目前僅支持同步調用,對異步調用(如任務隊列)其實也可以從相似的方式處理,

  1. 生產者創建任務并附加額外參數
  2. 任務隊列支持任務附加額外參數
  3. 消費者獲取任務并解析參數

Continuation-Local Storage

對 Node.js 應用代碼來說,所有請求都在一個線程中,因而難以區分 當前執行代碼在哪個 trace 上 (Request-Local)。

continuation-local-storage 部分解決 了這個問題,它通過 綁定回調函數的上下文 來區分請求。然而這增加了埋點代碼的復雜度,在為基礎庫實現采集代碼時,需要 非常小心的實現 和完備的測試用例,才能保證不出問題。

附錄

span 的完整數據結構

type Span {
    operationName: String
    startTime: Number
    duration: Number
    tags: [Object]
    logs: [Array]

    traceId: Long
    spanId: Long
    parentId: String
    sampled: Boolean
    baggage: Object
}

span 示例

{
  "type": "trail",
  "operationName": "upyun.account.get",
  "startTime": 1475035067586,
  "duration": 3,
  "tags": {
    "type": "ServerReceive",
    "service": "surume",
    "address": "10.0.5.58",
    "host": "10.0.5.58",
    "port": "8888",
    "protocol": "upyun.dendenmushi",
    "status": 0
  },
  "traceId": "5638971931279564310",
  "spanId": "1171987629847622065",
  "parentId": "17243618903848623758",
  "sampled": true,
  "baggage": {}
}

span 生命周期

 

 

來自:https://cattail.me/tech/2017/03/15/distributed-tracing.html

 

 本文由用戶 GarSirmans 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!