簡(jiǎn)述分布式追蹤和可觀察性

在當(dāng)前的分布式微服務(wù)架構(gòu)中,一個(gè)網(wǎng)頁(yè)中的簡(jiǎn)單請(qǐng)求可能在十幾個(gè)不同的微服務(wù)之間跳轉(zhuǎn),因此觀察或追蹤數(shù)據(jù)請(qǐng)求在應(yīng)用和網(wǎng)絡(luò)之間的路徑就顯得非常有價(jià)值了。
客戶端服務(wù)端示例
我們可以從一個(gè)簡(jiǎn)單客戶端服務(wù)端模型的應(yīng)用中來(lái)了解追蹤,比如說(shuō),我們有一個(gè)簡(jiǎn)單的 Web 服務(wù)器應(yīng)用程序,它在端點(diǎn) /hello 處接收一個(gè)請(qǐng)求,然后將一個(gè)靜態(tài)的字符串文本 "hello" 發(fā)送回客戶端。

從客戶端的角度來(lái)說(shuō),他們發(fā)送一個(gè)請(qǐng)求,等待一些時(shí)間(自己和服務(wù)器之間的網(wǎng)絡(luò)以及服務(wù)器處理請(qǐng)求的時(shí)間)接收到響應(yīng)。在服務(wù)器內(nèi)部,只有一個(gè)返回響應(yīng)數(shù)據(jù)的動(dòng)作發(fā)生。在一個(gè)應(yīng)用程序的上下文中,我們可以將這整個(gè)事務(wù)可視化為一個(gè) "span",并具有一個(gè)開(kāi)始和結(jié)束時(shí)間,以及有關(guān)所執(zhí)行操作類型的一些詳細(xì)信息或 "tag"。

現(xiàn)在客戶端知道了請(qǐng)求和響應(yīng)的相關(guān)信息,包括發(fā)送了什么數(shù)據(jù)、什么時(shí)候發(fā)送的、什么時(shí)候收到的以及收到的什么數(shù)據(jù),當(dāng)然服務(wù)器也知道一些類似的信息。但是有一個(gè)問(wèn)題是如果我們沒(méi)有手動(dòng)關(guān)聯(lián)兩個(gè)應(yīng)用程序之間的這些事件,就沒(méi)有一個(gè)簡(jiǎn)單的方法將它們鏈接在一起并獲得有關(guān)交互的追蹤信息。
追蹤請(qǐng)求
為了創(chuàng)建這兩個(gè)應(yīng)用程序之間交互的完整場(chǎng)景,我們需要一個(gè)外部的"觀察者"來(lái)收集有關(guān)客戶端和服務(wù)器交互的信息。然后,這個(gè)觀察者可以從整體上來(lái)查看應(yīng)用之間的交互信息。
在這個(gè)例子中,假設(shè)客戶端和服務(wù)器知道這個(gè)觀察者,并且每次發(fā)送或接收數(shù)據(jù)時(shí)都會(huì)上報(bào),他們會(huì)將交互中的每個(gè)相關(guān)事件和下面的信息告訴觀察者:
它們是誰(shuí)
它們?cè)谂c誰(shuí)交互
它們是否在發(fā)送或接收數(shù)據(jù)
它們正在發(fā)送或接收什么數(shù)據(jù)
該事件發(fā)生的時(shí)間點(diǎn)
這個(gè)交互過(guò)程可以用下圖進(jìn)行說(shuō)明:

下面是這個(gè)觀察者記錄的相關(guān)動(dòng)作:
客戶端在 time0 向服務(wù)器發(fā)送一個(gè)
/hello請(qǐng)求
服務(wù)器在 time1 收到來(lái)自客戶端的
/hello請(qǐng)求
服務(wù)器處理請(qǐng)求,并在 time2 時(shí)向客戶端回復(fù)了 "hello"
客戶端在 time3 接收到了服務(wù)器發(fā)來(lái)的 "hello"
觀察者現(xiàn)在能夠?qū)⑦@種雙方交互的事件對(duì)應(yīng)為一組連續(xù)的信息,我們可以從這兩個(gè)系統(tǒng)之間的簡(jiǎn)單整合中獲得一些有用信息:
什么地方發(fā)生的延遲最多?也許是在客戶端和服務(wù)器之間的網(wǎng)絡(luò),也許是服務(wù)器的處理時(shí)間延遲最大。
如果請(qǐng)求失敗了,它是否曾經(jīng)到過(guò)服務(wù)器,或者是在它和客戶端之間的鏈接中丟失了?
客戶端在 time0 向服務(wù)器發(fā)送一個(gè)
/hello請(qǐng)求
服務(wù)器在 time1 收到來(lái)自客戶端的
/hello請(qǐng)求
服務(wù)在 time1a 對(duì)來(lái)自客戶端的請(qǐng)求進(jìn)行解析
服務(wù)器運(yùn)行
buildResponse()函數(shù),在 time1b 向請(qǐng)求者提供相應(yīng)的響應(yīng)
服務(wù)器在 time2 時(shí)向客戶端發(fā)出 "hello" 的響應(yīng)
客戶端在 time3 接收到服務(wù)器發(fā)來(lái)的 "hello"
現(xiàn)在我們可以更深入地了解某些進(jìn)程在服務(wù)器上所花費(fèi)的時(shí)間了,甚至確切地了解哪個(gè)進(jìn)程在交互過(guò)程中可能已經(jīng)失敗了。
Spans 和 Traces
讓我們以上面觀察者記錄的數(shù)據(jù)為例,再添加兩個(gè)信息:
一個(gè)應(yīng)用程序在每次報(bào)告事件時(shí)都會(huì)產(chǎn)生的 Span ID
一個(gè) Trace ID,如果每個(gè)應(yīng)用程序收到的數(shù)據(jù)中包含這個(gè) ID,則會(huì)將其傳遞給它,如果沒(méi)有,將生成一個(gè)新的 ID 并上報(bào)
如果應(yīng)用程序?qū)⑦@些元數(shù)據(jù)附加到他們要發(fā)送給觀察者的事件上,那么現(xiàn)在觀察者記錄的數(shù)據(jù)會(huì)是這樣的:
客戶端在 time0 向服務(wù)器發(fā)送一個(gè)
/hello請(qǐng)求(TraceID: hellorequest1, SpanID: 1)
服務(wù)器在 time1 接收到來(lái)自客戶端的 /hello 請(qǐng)求(TraceID: hellorequest1, SpanID: 2) 2.1 服務(wù)器在 time1a 處解析來(lái)自客戶端的請(qǐng)求(TraceID: hellorequest1, SpanID: 3) 2.2 服務(wù)器運(yùn)行
buildResponse()函數(shù),在 time1b 向請(qǐng)求者提供相應(yīng)的響應(yīng)(TraceID: hellorequest1, SpanID: 4)
服務(wù)器在 time2 時(shí)向客戶端回復(fù) "hello"(TraceID: hellorequest1, SpanID: 5)
客戶端在 time3 收到服務(wù)器發(fā)來(lái)的 "hello"(TraceID: hellorequest1, SpanID: 6)
觀察者現(xiàn)在就可以使用 Trace ID 來(lái)將這一系列的事件歸納到一起,并使用 Span ID 將其分成獨(dú)立的呃部分。
由于這樣做可能會(huì)使組件的流程圖變得雜亂無(wú)章,因此可以將這些信息總結(jié)為一系列時(shí)間上的 spans。注意客戶端上處理響應(yīng)的函數(shù)被省略了,但你可以推測(cè)出這些函數(shù)的 spans 可能是什么樣的:

"hellorequest1" 這個(gè) TraceID 涵蓋了它下面所有的 spans,并將其發(fā)生的事件的追蹤信息進(jìn)行邏輯分組,如客戶端發(fā)送請(qǐng)求、服務(wù)器接收請(qǐng)求、服務(wù)器處理后將響應(yīng)發(fā)回客戶端。
客戶端在發(fā)起交互時(shí),會(huì)生成一個(gè) TraceID。然后如果存在 TraceID,交互中的每一個(gè)參與者都知道要把它傳遞出去,每個(gè)參與者也知道每次數(shù)據(jù)從上一個(gè)邏輯上下文或操作轉(zhuǎn)移到下一個(gè)邏輯上下文或操作時(shí),都會(huì)生成一個(gè)新的 SpanID。每個(gè)事件都會(huì)和這些 ID 一起報(bào)告給觀察者,這樣一來(lái),每個(gè)完整的事務(wù)都可以在邏輯上進(jìn)行分組,但一旦在觀察者那里聚合起來(lái),也可以被分解成獨(dú)立的部分。
追蹤多個(gè)應(yīng)用
現(xiàn)在我們將上面的示例擴(kuò)展為4個(gè)應(yīng)用程序。假設(shè)交互仍然從我們的客戶端開(kāi)始,但這次要向服務(wù)器發(fā)送一個(gè) /readinfo 的請(qǐng)求。服務(wù)器自己無(wú)法處理這個(gè)請(qǐng)求,所以它向另外兩個(gè)應(yīng)用程序各發(fā)送一個(gè)請(qǐng)求,我們稱他們?yōu)?SecurityService 和 InfoService。當(dāng)服務(wù)器收到一個(gè) /readinfo 請(qǐng)求時(shí),首先要詢問(wèn) SecurityService 服務(wù),請(qǐng)求者是否有正確的權(quán)限來(lái)讀取這個(gè)信息,如果成功的話,它就會(huì)調(diào)用 InfoService 來(lái)讀取信息,最后把信息發(fā)送給請(qǐng)求者。
我們把這個(gè)交互過(guò)程繪制出來(lái),可以標(biāo)注操作發(fā)生的順序,然后我們可以推斷出觀察者記錄的一系列事件。

上圖是觀察者根據(jù)每個(gè)應(yīng)用發(fā)給它的事件記錄的內(nèi)容,所有的事件都被列出來(lái)了,圖中的數(shù)字與列表中的數(shù)字相匹配,為了節(jié)省篇幅,我省略了時(shí)間戳和 ID:
客戶端向服務(wù)器發(fā)送 /readinfo 請(qǐng)求 - 服務(wù)器收到一個(gè) /readinfo 請(qǐng)求
服務(wù)器向 SecurityService 發(fā)送一個(gè) /access 請(qǐng)求 - SecurityService 收到一個(gè) /access 請(qǐng)求
SecurityService 對(duì) /access 請(qǐng)求做出響應(yīng) - 服務(wù)器收到來(lái)自 SecurityService 的 /access 響應(yīng)
服務(wù)器向 InfoService 發(fā)送一個(gè) /info 請(qǐng)求 - InfoService 收到一個(gè) /info 請(qǐng)求
InfoService 響應(yīng) /info 請(qǐng)求 - 服務(wù)器收到來(lái)自 InfoService 的 /info 請(qǐng)求
服務(wù)器響應(yīng)客戶端的 /readinfo 請(qǐng)求 - 客戶端收到 /readinfo 的響應(yīng)
我們可以看到只是增加了兩個(gè)應(yīng)用,被記錄的事件數(shù)量就增加了不少,我們可以將同樣的事件列表可視化為 spans,以便更好地查看 /readinfo 請(qǐng)求的執(zhí)行路徑:

注意,所有的事件仍然被歸納為同一個(gè) TraceID,但我們可以看到每個(gè)獨(dú)立的服務(wù)之間傳遞請(qǐng)求的 span,并共同處理最終結(jié)果到客戶端。
我們可以通過(guò)包括每個(gè)應(yīng)用程序中運(yùn)行的函數(shù)和子程序來(lái)進(jìn)一步擴(kuò)展 span 的粒度。
總結(jié)
上面的概念是微服務(wù)和分布式應(yīng)用的 Opentracing 規(guī)范中的術(shù)語(yǔ)。有許多開(kāi)源庫(kù)和軟件實(shí)現(xiàn)了 Opentracing 規(guī)范,你可以將這些直接集成到自己的應(yīng)用程序中,以獲得對(duì)微服務(wù)交互的強(qiáng)大觀察。
一旦你的應(yīng)用程序可以通過(guò) Opentracing 進(jìn)行通信,你仍然需要一個(gè)觀察者來(lái)記錄所有從應(yīng)用程序發(fā)出的事件。有很多系統(tǒng)都能實(shí)現(xiàn) Opentracing 規(guī)范,比如 Zipkin 就是這些流行的分布式追蹤系統(tǒng)的一個(gè),可以自行安裝使用。它在我們的例子中扮演的就是觀察者的角色,并從遵守 Opentracing 標(biāo)準(zhǔn)的應(yīng)用程序中獲取追蹤數(shù)據(jù),以提供應(yīng)用程序追蹤的交互式可視化。

如果你想了解更多關(guān)于 Opentracing 的信息,可以查看官網(wǎng),有對(duì)應(yīng)的規(guī)范文檔和使用指南。你也可以嘗試使用 Zipkin,它的庫(kù)支持幾乎所有的編程語(yǔ)言。
原文鏈接
https://medium.com/swlh/tracing-and-observability-9ab98438d773
https://opentracing.io/specification/
https://zipkin.io/
