摘要
本人近日在參加阿里雲原生的鏈路追蹤系統設計比賽,在比賽中感覺收穫頗多,包括一些工具框架的使用,例如okhttp
以及springboot
,同時也對於如何使用springboot
有了更進一步的認識。本博客就自己的收穫以及對於賽題進行簡單的梳理。比賽的詳細介紹詳見比賽首頁
鏈路追蹤
本次比賽是需要實現鏈路追蹤系統,關於鏈路追蹤的詳細介紹可參加官網的詳細介紹,本人簡單說一下我的理解,在本賽題中,鏈路實際上就是多個同處一個事務或流程中的http調用。因爲對於網絡系統編程(或者雲原生or分佈式),單個事務的執行勢必涉及到多個不同系統,而體現在具體執行中,就是一個個不同的http調用組成了一個事務的執行。本質上,同屬一個調用鏈路的http span
組成了一個有向無環圖,本次比賽爲了簡化,直接將同屬相同調用鏈路的http span
用全局唯一的traceId
進行了標識,也就是不需要參賽選手進行調用鏈路的組建,只需要將數據流中所關心的調用鏈路給出即可。
數據格式
比賽中給出的http span
是以http
流給出的,每條http span
佔據一行,數據格式如下:
traceId | startTime | spanId | parentSpanId | duration | serviceName | spanName | host | tags
具體各字段意義解釋如下的:
- traceId:全局唯一的Id,用作整個鏈路的唯一標識與組裝
- startTime:調用的開始時間
- spanId: 調用鏈中某條數據(span)的id
- parentSpanId: 調用鏈中某條數據(span)的父親id,頭節點的span的parantSpanId爲0
- duration:調用耗時
- serviceName:調用的服務名
- spanName:調用的埋點名
- host:機器標識,比如ip,機器名
- tags: 鏈路信息中tag信息,存在多個tag的key和value信息。格式爲key1=val1&key2=val2&key3=val3 比如 http.status_code=200&error=1
比賽給了兩條http流,而且爲了簡化比賽,假定相同traceId
的span
前後不會超過2萬條。
賽題分析
在不考慮數據量的條件下,最樸素的解決方法當然就是在客戶端將http流全部緩存下來,然後存入HashMap<String,List<String>>
中,其中Key
是符合上報條件的traceId
,Value
是與之對應的所有http span
,再按照http span
中startTime
屬性升序排序即可。由於數據量龐大,顯然這樣的方案不可行,但是我們注意到題目中給出的提示,假定相同traceId
的span
前後不會超過2萬條,這樣我們實際上就可以將龐大的數據流分割成基本單位爲2萬大小的batch
來逐步的處理,爲了說明方便,給出如下的示意圖:
如上圖所示,我們將數據流按照上述形式進行了分割以後,實際上就可以轉化爲樸素的解法了,處理紅色batch時,需要將前後各一個batch進行彙總,共同計算結果,這是由於相同的traceId
有可能橫跨兩個batch,但是出現在前面還是後面是不確定的,因此需要一起計算。需要注意的是,由於相同的調用鏈路有可能出現在不同的數據流中,我們需要將當前batch多個數據流中有問題的traceId
全部彙總以後,在進行彙總計算。說明了整體的算法思路之後,我們分析一下時間複雜度,假設總的數據量爲N,由於每個batch都需要重複計算前後各一個batch,那麼總的複雜度爲3N。
賽題交互邏輯設計
由於比賽要求實現客戶端和服務端,實際上就是將過濾和計算模塊解耦,方便以後的系統設計。客戶端需要進行存在問題的traceId
過濾彙報以及數據的緩存服務,後端則負責將客戶端上報過來的有問題的traceId
拉取客戶端當前緩存的數據,然後在進行彙總以及最終的上報。由於涉及到複雜的http交互,繪製如下的示意圖明確交互過程:
賽題具體實現
整體設計流程基本上都體現在上圖中了,然後下面闡述實現中的幾個關鍵點:
-
由於調用鏈路並行存在多條數據流中,我們根據過濾出的
traceId
後端進行計算時,一定要拿到多條數據流的彙總結果,這樣才能保證結果的正確性。還有就是當客戶端將當前batch和下一個batch過濾結果都彙總完畢時,纔可以進行最終結果的彙總。其目的是爲了保證traceId
出現在不同的batch
這一現象。 -
前後端傳遞數據使用
okhttp
模塊和fastjson
,分別爲我們提供了http通信和對象的序列化服務,而http的請求處理我們使用springboot
來完成。 -
爲了方便客戶端和後端的交互,我們設計了
TraceIdBatch
類,方便我們判斷前後端的狀態。其UML圖如下:
其中batchPos是爲了標記backend正在處理的batch序號,processCount用來標記已彙總數據裏的個數,當已彙總的數據流大於等於提供的數據流條數,標誌着客戶端當前batch過濾彙報的完成,backend就可以根據彙總的結果向client拉取結果,進行計算。