呼叫鏈系列一:解讀UAVStack中的呼叫鏈技術

iEven發表於2019-02-17

一週一更,UAVStack又來例行推新啦~~~在上週的推送中,我們介紹了呼叫鏈技術中的日誌聚合、分散式跟蹤及二者的關聯運用,相信大家已經對呼叫鏈有了基本瞭解。本週,我們將繼續介紹呼叫鏈的模型設計與模型時序圖,小夥伴們上車啦


在分散式線上服務中,一個請求需要經過多個系統中的多個模組,可能需要多達上百臺機器的協作才能完成單次請求。在這種場景下,單靠人力已經無法掌握整個請求中各個階段的效能開銷,更無法快速定位系統中的效能瓶頸。發生故障時,通常需要多個團隊合作,檢視大量日誌才能確定問題。

舉個栗子

作為一個在職場摸爬滾打很多年的資深工程師,程式猿小亮面對的系統設計可能是這個樣子的:

1
藉助良好的系統設計和編碼規範,對於一般有問題的請求處理,小亮能夠憑藉對多個系統的瞭解,在翻閱大量日誌檔案(前提是日誌輸出也是規範的)後定位到問題。隨著使用者數量的增加,系統複雜度也越來越高,小亮的大部分時間都花費在團隊溝通工作上了。

這時,小亮可能會想,如果有一個工具能夠把每次請求經過的系統都記錄下來,並把每個節點的消耗時間和處理類等資訊也抓取出來,那這個世界該多麼美好啊。

一個偶然的機會,小亮瞭解到了UAVStack中的呼叫鏈功能,能夠在對業務程式碼沒有任何侵入的前提下輕鬆解決他的難題。下面就讓我們一起開啟神奇的呼叫鏈探索之旅吧。

UAVStack呼叫鏈技術棧支援

2

效果展示

輕型呼叫鏈展示詳情:

3
重呼叫鏈開啟以後請求報文體抓取檢視:
4
更多使用說明請參閱官網: 使用者指南

具體實現

UAVStack呼叫鏈實現分為模型設計、服務端資訊收集(輕/重)、方法級資訊收集(輕/重)、客戶端資訊收集(輕/重)、呼叫鏈協議設計(輕/重)、呼叫鏈上下文傳遞、呼叫資訊記錄及傳遞、呼叫資料統計處理等。由於篇幅限制,本期暫時只分享其中的模型設計及實現呼叫鏈模型時序圖。

在參考現有經驗並結合具體業務場景需求的基礎上,我們抽象出了如下呼叫鏈模型:

呼叫鏈後設資料:

  1. SpanEndpointType:呼叫型別(Root(“E”),Service(“S”), Client(“C”), Method(“M”))。

    ☆ Root指本條呼叫鏈中的第一個節點,即一條呼叫鏈的開始位置,可以是一個服務請求,一次httpclient呼叫等;

    ☆ Service指當前呼叫鏈中非第一個節點且是系統對外提供的服務,如使用者登入服務;

    ☆ Client指當前呼叫鏈中非第一個節點且是當前系統與外部溝通的一種途徑,如httpclient、mongoclient等;

    ☆ Method值當前呼叫鏈中非第一個節點且是系統中的一個函式,如日誌數出函式等。

  2. traceId:呼叫鏈唯一識別符號。

  3. spanId:一條呼叫鏈中當前節點的呼叫順序(與SpanEndpointType 結合唯一);spanId採用分層設計,形如1.2.1,既能表示呼叫順序同時又能反應所才呼叫鏈層級。

  4. parentId:一條呼叫鏈中當前節點的父呼叫節點。

呼叫鏈繪製規則:

  1. 呼叫者(服務、web)最初呼叫(無父呼叫)記為開始節點E,並生成唯一呼叫鏈ID,traceID;
  2. 系統內應用元件呼叫(如httpclient,方法呼叫等),spanId末尾數字加1(若為第一個則末尾加.1);
  3. 系統間呼叫(如A服務呼叫B服務),A服務與B服務span資訊只有SpanEndpointType不同(分別對應span的兩個端)。

舉個栗子

使用者小明想通過網路獲取一些知識,通過網路,他進入了系統O。服務O中部署了服務A和B,A服務使用httpclient與B通訊,B服務先會與Redis互動然後和MySQL互動,最後系統O將小明感興趣的內容返回給小明。

在完成此次請求的過程中,UAVStack抽象出如下呼叫鏈模型:

  1. 小明(下圖中的呼叫方)通過門戶訪問了A服務,此時呼叫鏈生成唯一traceId並將當前節點的SpanEndpointType置為N(第一個節點的意思),spanId置為1(當前呼叫層中的第一個節點),parentId置為E(沒有父節點的意思);
  2. A服務通過httpclient向B服務發起一次http請求,此時呼叫鏈後設資料如下traceId(沿用父節點id);1.1(spanId末尾加.1,因為為第一次呼叫);1(parentId父節點的spanId);C(呼叫型別記錄為C客戶端呼叫);
  3. B服務接收到來自於A服務通過httpclient的呼叫,此時呼叫鏈後設資料如下traceId(沿用初始呼叫時id);1.1(spanId沿用傳遞過來的spanId);1(parentId沿用傳遞過來的parentId);S(呼叫型別記錄為S服務端處理請求);
  4. B服務先查詢Redis,此時呼叫鏈後設資料如下traceId(沿用初始呼叫時id);1.1.1(spanId末尾加.1,因為為第一次呼叫);1.1(parentId父節點的spanId);C(呼叫型別記錄為C客戶端呼叫);
  5. B服務又發起對MySQL的查詢,此時呼叫鏈後設資料如下traceId(沿用初始呼叫時id);1.1.2(spanId末尾數字加1,因為為非第一次呼叫);1.1(parentId父節點的spanId);C(呼叫型別記錄為C客戶端呼叫);
  6. 處理結束,呼叫鏈記錄下了整個過程中的呼叫資訊。

5
呼叫鏈時序圖

6

  1. UAVServer:中介軟體增強框架,提供在中介軟體的不同生命週期進行劫持的能力,即中介軟體劫持技術,如tomcat webcontainer啟動開始時刻等;
  2. JEEServiceRunGlobalFilterHandler:藉助中介軟體劫持技術延伸出的全域性filter,能夠攔截所有經過中介軟體(tomcat等)的請求;
  3. ServiceSpanInvokeChainHandler:呼叫鏈中專注處理歸為Service型別節點的handler; ClientSpanInvokeChainHandler:呼叫鏈中專注處理歸為Client型別節點的handler;
  4. XXAdapter:泛指呼叫鏈中所有的adapter,提供在handler(分為Service、Client、Method三種handler,圖中省略了Method型別)執行動作before和after時刻運算元據的能力。

如此,在對使用者程式碼無任何”侵入”的前提下便完成了呼叫鏈的生成,生成過程大致如下:

  1. 在JEEServiceRunGlobalFilterHandler的doRepuest中包裝解析請求;
  2. xxAdapter中的before對資料進行適配;
  3. xxHandler處理對應範圍內(Service、Client和Method)內請求資料;
  4. xxAdapter中的after對資料進行整理或記錄;
  5. 在JEEServiceRunGlobalFilterHandler的doResponse中返回處理過後請求。

本文的主要目的是讓大家初步瞭解一條呼叫鏈繪製的大致生命週期,具體實現將在下期分享中詳細介紹,歡迎大家繼續關注碼完程式碼還要辛苦碼字的技術小哥哥~~~

官方網站

開源地址

UAVStack已在Github上開放原始碼,並提供了安裝部署、架構說明和使用者指南等雙語文件,歡迎訪問-給星-拉取~~~

掃描下方二維碼,關注一個不會讓你失望的公眾號

呼叫鏈系列一:解讀UAVStack中的呼叫鏈技術

相關文章