導讀:有一天我們接到這樣一條客訴“你們的收銀軟件最近特別慢,嚴重影響我們的收銀效率,再不解決我們就不用了”,我相信大家應該都遇到過這種問題,即使現在沒遇到,將來一定會遇到的,那遇到了怎么辦呢?就這個話題我們今天一起來聊一聊。
關鍵詞:分布式,鏈路追蹤
靠人終究靠不住
不知道大家是怎么處理開頭提到的那種問題的呢?最簡單粗暴的辦法就是把相關人員集中到一個會議室里面對數據,怎么對呢?
客戶端開發人員:我查了日志,客戶端的請求過程一共用了5s,請求是從幾點幾分幾秒發起的,你們查下服務端的日志;
交易系統開發人員:我這邊是幾點幾分幾秒收到的請求,交易系統一共花了4s多一些,其中調用支付網關花了將近4s,網關那邊看下日志吧;
網關開發人員:我這邊是幾點幾分幾秒收到的請求,網關一共花了3s多一點,大部分時間都花在了調用第三方上;
估計大多數人最開始都是這么處理此類問題的,簡單粗暴。但如果三天兩頭給你來這么一下子你還受得了嗎?每天給你幾百個上千個訂單號讓你對數據,你還能抽時間寫代碼嗎?估計連帶薪上廁所的時間都沒了吧。最后這個問題可能傳到了領導那里,領導一般喜歡要全局報表數據,你怎么給他出這個報表?是不是束手無策,突然有點想換工作了,哈哈。我們還真是接到過這種需求,一堆人在那里awk然后就沒有然后了。
“當一件事情成為一件常態,那意味着我們可能需要一件工具來解放自己了,靠人終究是靠不住的”,就在這種背景之下我們決定引入一個調用鏈追蹤的工具來解放我們,也就是今天的主角jaeger。關於jaeger的說明網上很多,推薦去官網系統的了解一下 https://www.jaegertracing.io,我這里只是把搭建過程和使用上的一些心得分享出來和大家一起交流。
jaeger架構
直接引入一張官網的圖
jaeger組件介紹:
jaeger-client:jaeger 的客戶端,實現了opentracing協議;
jaeger-agent:jaeger client的一個代理程序,client將收集到的調用鏈數據發給agent,然后由agent發給collector;
jaeger-collector:負責接收jaeger client或者jaeger agent上報上來的調用鏈數據,然后做一些校驗,比如時間范圍是否合法等,最終會經過內部的處理存儲到后端存儲;
jaeger-query:專門負責調用鏈查詢的一個服務,有自己獨立的UI;
jaeger-ingester:中文名稱“攝食者”,可用從kafka讀取數據然后寫到jaeger的后端存儲,比如Cassandra和Elasticsearch;
spark-job:基於spark的運算任務,可以計算服務的依賴關系,調用次數等;
其中jaeger-collector和jaeger-query是必須的,其余的都是可選的,我們沒有采用agent上報的方式,而是讓客戶端直接通過endpoint上報到collector。
搭建jaeger
因為我們的應用服務都是采用容器部署的,所以我們的jaeger服務也沿用以往的風格。
docker啟動jaeger-collector
docker run -d --rm -p 14268:14268 -p 14269:14269 -e SPAN_STORAGE_TYPE=elasticsearch -e ES_SERVER_URLS=http://10.200.46.229:9200 jaegertracing/jaeger-collector:1.11
docker啟動jaeger-query
docker run -d --rm -p 16686:16686 -p 16687:16687 -e SPAN_STORAGE_TYPE=elasticsearch -e ES_SERVER_URLS=http://10.200.46.229:9200 jaegertracing/jaeger-query:1.11
應用程序接入
接下來就是如何讓調用鏈條上的各端接入了,這里只需要把握一個原則就好,“盡量讓接入方無感知,沒有侵入性”,這里簡單說下我們的接入方式:
- 客戶端接入:客戶端采用okhttp 攔截器的方式接入,使用請求頭傳遞trace上下文,這里還可以和okhttp 的EventListener配合起來獲取一些網絡層面的指標,比如dns解析時間,連接發起時間等等;
- web程序接入:web端采用springmvc攔截器方式接入,從http請求頭里面來提取trace上下文,然后基於上下文構建一個springmvc的span,記得在請求結束的時候finish奧,否則調用鏈數據可能會長這樣:
- RPC框架如何集成:一般RPC框架都會提供一些擴展點讓使用者來做一些框架集成的事情,拿dubbo來說可以采用Filter和隱示傳參的方式來實現請求上下文的傳遞;
- 外部調用如何集成:有一些調用是基於sdk或者httpclient調用的,這類調用我們如何植入調用鏈的邏輯呢?這里不得不佩服AspectJ的強大了,為了避免你少走彎路我還會推薦你去了解一下“spectj-maven-plugin”這個maven插件,什么?不是基於spring的那一堆注解就可以了嗎,為什么還要引入maven來干這事。估計你還需要去了解一下運行期植入和編譯器植入的相關概念以及適用場景。
具體你要把Span包裝成什么樣就靠你自由發揮了,但是不要太離譜,建議參考下這個https://opentracing.io/docs/overview/spans/。
上線
上線前問自己幾個問題,我的攔截器寫的是否健壯,拋異常了不會影響正常調用吧?是否需要評估一下數據量?別一上線把后端存儲打死了。
使用jaeger-quey來檢索調用鏈
- 先選擇一個service然后針對這個service做一些復雜的檢索,比如針對某個標簽,操作的耗時等;
2.如果有滿足條件的數據右邊會展示出結果
上面圖中分別展示了兩條支付的調用鏈路,一條成功了,一條失敗了,你可能會問:jaeger是怎么判斷成功失敗的呢?簡單來說就是通過特殊的標簽,直接甩給你一篇opentracing的文檔看完就懂了 https://github.com/opentracing/specification/blob/master/semantic_conventions.md。
3.查看調用鏈詳情
4.查看依賴關系,以及調用次數
也許你服務也搭好了,調用鏈數據也看到了,但就是看不到這個調用關系圖,別急你去這溜達一圈就知道了https://www.jaegertracing.io/docs/1.11/faq/。
好吧,今天就到這,大周六的晚上抽一點時間來梳理一下最近的工作,還希望對各位有一點點的幫助。