一、分布式鏈路追蹤可以做什么?
1.1:簡單集群架構&微服務架構
先來看下最簡單的網站集群架構圖:
圖1
在這個圖里,存在從1~n個服務器,通過負載均衡器SLB進行請求分發,在每個服務器里,都做同一件事情。
現在來看下這個系統的具體業務邏輯(就是圖1中每台服務器執行的邏輯,這里是假設其中一個業務接口的處理,真實系統中可能存在n多業務接口):
圖2
圖2是對系統中某一個接口(API)的邏輯做了描述,假設處理流程就是請求一次Redis服務,然后做下處理,然后再請求下Memecached服務,在做下業務處理,后續可能還有其他各種業務邏輯,我們統稱為邏輯塊。
現在假設這個接口存在性能問題,那么找對應開發負責人排查是比較容易的,因為服務器只執行一套邏輯,瓶頸點一定發生在當前接口對應代碼里的某個點,那么就找接口對應的負責人去排查優化即可。
但是當業務發展到一定的程度,比如上述單系統邏輯越來越復雜(業務接口越來越多,邏輯越來越復雜),就會造成很多我們不願意看到的問題發生:
①每一次微小的改動都需要整體走一次打包、發版的流程,對測試也是種負擔,因為n多個人如果同時開發不同的功能,這時候就會對測試和發布流程造成很大的困擾。
②如果因為做某次活動,某一個接口可能引入大量請求,需要緊急擴容,那么只能對整體擴容(因為該接口與其他接口都處於同一個系統中)。
③系統各模塊高度耦合,不適合多人開發和維護。
簡單集群帶來的問題會隨着系統復雜度的提升,維護成本變得越來越大,基於此,便有了微服務架構(微服務是一種架構思想,簡單來說就是將復雜龐大耦合度高的系統按照功能特性拆分成一個個獨立的系統,通過網絡互相通信,這種架構可以借助RPC框架(比如grpc、dubbo)實現拆分。當然,熟悉的HTTP框架也可以做到(比如okhttp),但是受限於HTTP協議,性能可能並沒有普通RPC框架高,比如grpc采用HTTP2應用層協議進行編解碼,這個協議相比HTTP1來說,支持數據流的標記,可以在一個長連接上做N多請求和接收的並發處理,屬於全雙工網絡通信,這點放到HTTP1就很難做到)。
結合圖2,我們來簡單按照業務划分一下服務,可以將A代碼塊里的邏輯抽象成A服務,將B代碼塊里的邏輯抽象成B服務,當然還有可能有其他n多細化的服務,網關層API(負責聚合信息以及業務處理的模塊,對應上面簡單集群里的具體接口),服務注冊與發現、SLB等。
下面再來看一下被拆分后的架構圖:
圖3
這張圖是一個很簡單的微服務化的架構圖,圖中虛線部分都是在各服務啟動時或者運行期發生的調用,負責注冊與發現(如zookeeper、Eurake等都可以作服務注冊與發現,這里不再細說,只關注實線部分即可)。
這種架構很好的解決了普通集群架構帶來的問題(參考上述①、②、③),微服務架構的好處:
①降低了系統(邏輯塊)間的耦合度,可以獨立開發、獨立部署和測試,系統間的邊界明確,可以細分相關負責人,開發效率大大提升。
②如果因為做某次活動,某一個接口可能引入大量請求,需要緊急擴容,那么只需要將該接口涉及到的服務進行擴容即可,而不是像之前那樣整體擴容,降低了維護成本(某種意義上的降低,維護人員要足夠多,每個人去負責自己的小模塊,如果一個公司只有一個維護人員,微服務反而是在加重維護人員的工作:)。
③提高了系統(邏輯塊)的復用性,比如上面的服務A做自己的事情,萬一以后有個API仍然需要A邏輯塊,那么該API只需要再次調用A服務即可(實際應用當中的例子:用戶服務)。
④服務化以后,每個服務甚至可以用不同的語言來實現(存在支持跨語言的RPC框架,比如grpc),一個公司大了以后,可能存在語言差異,有的組使用JAVA,有的組用Go,通過服務化的方式,來將兩個不同語言的系統互聯。
上面簡單介紹了普通集群架構和微服務架構,同樣的,微服務化也意味着系統調用的復雜化,有可能一次API的調用對應大批量的服務調用,服務方自己又有一堆服務調用,那么針對這種問題,我們來模擬一次復雜的API調用(注冊與發現服務已隱藏),如圖4所示:
圖4
這是模擬了一次微服務架構中比較復雜的系統調用。⚠️注意:圖畫的有點歪,微服務架構的設計目標是要高度解耦,每個獨立的服務最好都有一份自己獨立的資源訪問,比如服務A只訪問A業務相關的數據庫和緩存等資源,圖中針對這些資源划分做的很糙
那么現在如果這個較復雜的鏈路調用上的其中一環發生了性能瓶頸,拖慢了整個API的調用,比如圖中的“慢”標識,現在我們再來模擬一下這個性能問題的排查過程(過程相當鬼畜):
負責API編寫的同學發現API的響應時間總是達不到預期,自己debug發現導致性能問題的原因是服務C,於是找到了服務C的負責人C同學,C同學緊接着排查,發現原來是服務D的調用過慢,於是又跑去找D同學,D同學收到C同學的反饋,然后去查自己的服務,發現自己調用的服務E響應緩慢,於是D同學又跑去找E同學,E同學緊接着排查,發現原來是自己這里調用的Redis_02服務有問題,然后自己排查,如果不是自己調用方式有問題,接下來還可能去聯系對應的Redis_02相關維護人員幫助檢查瓶頸點。
對比簡單集群方式中的單系統性能問題排查,微服務針對此類問題的排查簡直是一場噩夢,這其中涉及到的人跟瓶頸節點的深度成正比,因為任何一個環節都有可能存在性能問題,而拖慢整個進度的根源未知,那么有沒有一種工具可以完成跨服務跨系統的去跟蹤這次的調用鏈路呢?
1.2:分布式鏈路追蹤
結合上面的問題,分布式鏈路追蹤系統就誕生了,來看下Google的這篇文章:Dapper,大規模分布式系統的跟蹤系統,可以對分布式鏈路追蹤系統有個系統的認識。
單純的理解鏈路追蹤,就是指一次任務的開始到結束,期間調用的所有系統及耗時(時間跨度)都可以完整記錄下來,比如上面圖4的例子,假設總耗時100ms,存在瓶頸鏈路C-->D-->E-->Redis02,如果鏈路追蹤系統做好了,鏈路數據有了,借助前端解析和渲染工具,可以達到下圖中的效果:
圖5
可以看到從API的調用開始到每個涉及到的系統調用以及系統內部的調用鏈路和時間跨度被直觀的展示出來了,通過上圖,可以看到時間跨度最長的就是Redis_02,該服務的調用間接拖慢了E服務、D服務、C服務的響應,最后由C服務直接導致API整體響應緩慢,通過這個圖,就可以直接找到對應的責任人去排查對應的問題,而不是像之前那樣找一群人。
二、分布式鏈路追蹤系統的組成
類似很多監控系統,該系統也分為基礎數據采集+數據存儲+前端展示幾個部分,來看下一個分布式鏈路系統的基本結構:
圖6
上圖比較粗略的展示了一個完整的鏈路追蹤系統的結構,本篇文章不會介紹具體的鏈路追蹤系統的實現,可以先簡單將該系統理解為接收+存儲鏈路數據的作用,前端也一樣,可以先簡單理解為請求鏈路系統API,API內部負責讀取db,並將數據封裝成前端需要的格式,前端負責繪制圖5中的頁面即可(只要數據結構約定好,對於專業的前端工程師做出圖5的效果是很容易的,當然網上也有現成的前端工具)。
本篇文章主要介紹鏈路追蹤究竟是什么,可以解決什么問題,下一篇將會詳細介紹“鏈路數據采集SDK”,因為這一部分是跟業務組件開發人員直接掛鈎的,下一篇會說明鏈路追蹤的數據結構、如何做到鏈路數據的采集和上報、如何做到跨服務的鏈路追蹤。
開始前可以先了解一個標准:OpenTracing語義標准
這里面講了兩個很重要的概念:Tracer和Span,一個Tracer認為是一次完整的鏈路,內部包含n多個Span,Span表示具體的一次調用,圖5中就是一次完整的調用鏈路,里面每個耗時條都是一個Span,Tracer和Span存在一對多的關系(看到這里,圖6中的鏈路追蹤API的實現可以認為是根據Tracer的id聚合一批存在父子關系的Span封裝成定義好的數據結構傳給前端進行渲染的),根據圖5,可以知道Span與Span之間又存在父子關系。
具體的實現方案和實現方法,下一篇會詳細介紹~下一篇會圍繞着圖5進行展開。