架構設計(14)-- 分佈式鏈路跟蹤:我們自研log2組件

最近升級改造我們鏈路跟蹤系統Log2,花了將近一週時間調研一些開源的鏈路跟蹤系統,在此調研過程中,做了一些筆記和總結分享出來,若有誤請指教,謝謝。本博文還在更新中。

分佈式鏈路跟蹤1: 理論知識

分佈式鏈路跟蹤2:Zipkin實踐

分佈式鏈路跟蹤3:skywalking原理和實踐

分佈式鏈路跟蹤4:自研組件Log2

 

一、log2背景


我們在2016年開始基於dubbo做分佈式服務化,在這個階段,系統頻繁出現問題和故障,出現問題後又不能快速定位問題。比如dubbo偶爾超時問題,我們都無法定位問題在哪,一開始以爲是我們使用dubbo不當或者dubbo框架性能問題。我們通過tcpdump抓包分析是不是網絡問題,最後也沒有定位到是問題的原因。這時我們急需一個鏈路跟蹤系統,能簡單滿足如下需求:

1、全局globalId. 根據globalId可以查詢所有相關日誌。
2、跟蹤記錄入口方法action參數和返回結果。
3、記錄響應時間。
4、記錄某個方法的執行時間。
5、跟蹤組件不能影響業務需求。

我們簡單調研一些開源系統,都沒有滿足第2條需求,2016年的skywalking也還沒有那麼流行。而且我們服務幾乎都是JAVA開發,因此最後我們決定自研組件,腦子一熱就命名爲log2。

 

二、基本原理


JAVA分佈式鏈路追蹤其實原理都很簡單:

1、上下文傳遞:

每個請求過來,主線程都會分配一個線程newThread 來執行請求。把請求的context存到該分配線程newThread   的ThreadLocal裏面(一般使用InheritableThreadLocal《線程ThreadLocal和TransmittableThreadLocal區別》),然後context隨着線程一路傳遞下去就行。

2、攔截請求

1、實現org.springframework.web.servlet.HandlerInterceptor接口實現攔截器。
2、利用Spring的切面(AOP)實現攔截。
例如:zipkin的鏈路跟蹤是通過攔截器實現,在服務入口處添加攔截器,獲取上級請求過來的鏈路信息span,然後構建新span。並將新span信息存在當前線程ThreadLocal中。使用接口攔截器可以獲取攔截的請求對應的類和方法的相關信息,但缺點在於該handler對象無法獲取具體執行方法的參數信息。而基於AOP實現的攔截器可以獲取到具體參數。

因此最後我們選擇AOP實現。

3、跨服務傳遞跟蹤信息

開源跟蹤組件一般都是通過修改rpc header來實現傳遞跟蹤信息,由於我們已經記錄了方法參數,因此在參數裏面傳遞全局globalId。

 

三、自研log2歷程


1.0版本主要內容:

1、全局ID.
2、跟蹤入口方法action參數和返回結果。
3、記錄響應時間。
4、記錄某個方法的執行時間。
5、跟蹤組件不能影響業務需求。
系統主要由:
log2-utils:採集器,採集跟蹤信息。
log2-collector: 日誌接收中心,並做些格式化落地,方便存儲。
ELK:日誌展示查詢

組件開發完成後,應用在一些不重要的服務,在這期間我們也不敢大面積的推廣使用,遇到不少問題:
1、ThreadLocal的問題:ThreadLocal內存泄漏,沒有及時清掉log2Context。
2、父子線程傳遞問題:ThreadLocal不支持繼承性,比如new Thread() 新線程無法傳遞log2Context,改InheritableThreadLocal就沒問題。《線程ThreadLocal和TransmittableThreadLocal區別
3、線程池問題:InheritableThreadLocal的log2Context在線程池裏面重復使用,需要使用Alibaba的一個庫解決了這個問題https://github.com/alibaba/transmittable-thread-local

 

1.xx.x 版本內容:

2017年底log2使用已經比較穩定,但是還沒有大規模推廣使用,系統難於定位問題仍然存在。一次又一次無休止的故障總結會後,我們強制推廣log2,同時阿里雲的日誌服務產品也正好出來,我們放棄ELK, 日誌全部存儲到日誌阿里雲日誌服務上。(阿里雲日誌服務產品開始收費比較低,  按1T/天的日誌量,我們自己搭建ELK, 單服務器費用就花費不少。)
在這期間,我們:
1、優化了log2-collector,分析方法參數和方法返回結果。
2、使用阿里雲的監控服務,監控log2日誌的響應時間。
遇到的問題:
log2-collector雖然也是異步處理,但接收併發過大,QPS到達萬級別,導致log2-utils發送隊列堆積,造成某個應用服務內存泄漏。我們解決辦法是在應用服務器部署log2-collector,應用服務器直接通過127.0.0.1來發送。

2.x版本內容:

2019年,我們優化升級到2.0 :
1、log2-util可以過濾方法參數,比如圖片參數,圖片數據太大,影響我們存儲空間。
2、全局globalId由網關nginx生成,log2-util通過HttpRequestContext來獲取。
這樣通過globalId實現了nginx日誌和log2日誌關聯。

目前2.0版很穩定,log2-utils性能消耗很低,在QPS爲500情況下,時間消耗大概爲3~5ms。
整體架構如下:

 

四、log2-utils的架構圖


採集組件log2-utils架構圖:
主要組成:boostrap啓動模塊,log2config配置模塊,log2Aspect的AOP攔截模塊,Log2Context上下文模塊,
ProcessControll異步日誌處理模塊

1、服務啓動:Log2Initialize 通過ApplicationListene監聽ContextRefreshedEvent事件,完成log2相關配置。

2、處理請求:log2Aspect攔截請求:初始化log2Context模塊,執行業務方法,記錄跟蹤日誌後,調用ProcessControll模塊異步處理日誌,包括:完善日誌信息(ip,服務名),通過http發送到log2-collector。

 

五、後續3.0版本


我們最近調研了zipkin, skywalking等優秀的開源跟蹤系統,目前還不能遷移到開源跟蹤系統的原因:

1、缺少參數信息:開源跟蹤系統都沒有方法參數記錄。
2、服務費用成本:使用開源跟蹤系統,需要自建ES集羣,日誌量比較大,需要幾十臺服務器,成本比較高。
3、人力成本比較大:遷移肯定需要有人來完成。

回到我們開頭,我們升級改造log2,升級到3.0版本:
1、優化代碼結構,考慮開源。
2、結合系統基礎框架,跨服務傳遞parentId。
3、和log4j2,logback日誌組件集成使用。
4、日誌可選擇落地,然後通過logstash發送到log2-collector

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章