91无码高清视频在线播放|亚洲最大成人在线资源|日本黄色免费调教网站|亚洲春色一区二区三区|国产一级一区二区三区|成人免费 做受电影无码

歡迎訪問(wèn)漢海網(wǎng),帶你進(jìn)入知識(shí)的海洋!

鏈路狀態(tài)算法的主要思想(怎么理解分布式鏈路追蹤技術(shù))

方應(yīng) 分享 時(shí)間: 瀏覽:0
—1—為什么需要鏈路追蹤

在學(xué)習(xí)分布式鏈路追蹤之前,我們需要先理解這項(xiàng)技術(shù)產(chǎn)生的背景,以及它能夠幫我們解決哪些棘手問(wèn)題。

提到分布式鏈路追蹤,我們要先提到微服務(wù)。相信很多人都接觸過(guò)微服務(wù),這里再回顧一下基本概念。

微服務(wù)是一種開發(fā)軟件的架構(gòu)和組織方法,它側(cè)重將服務(wù)解耦,服務(wù)之間通過(guò)API通信。使應(yīng)用程序更易于擴(kuò)展和更快地開發(fā),從而加速新功能上線。

微服務(wù)演變-亞馬遜云

加速研發(fā)快速迭代,讓微服務(wù)在業(yè)務(wù)驅(qū)動(dòng)的互聯(lián)網(wǎng)領(lǐng)域全面普及,獨(dú)領(lǐng)風(fēng)騷。但是,隨之而來(lái)也產(chǎn)生了新問(wèn)題:當(dāng)生產(chǎn)系統(tǒng)面對(duì)高并發(fā),或者解耦成大量微服務(wù)時(shí),以前很容易就能實(shí)現(xiàn)的監(jiān)控、預(yù)警、定位故障就變困難了。

我拿研發(fā)的搜索系統(tǒng)的經(jīng)歷,結(jié)合場(chǎng)景給大家聊聊(曾經(jīng)這套系統(tǒng)要去抗峰值到日均10億PV、5千萬(wàn)UV的高并發(fā))。

機(jī)票搜索演示圖

比如搜索機(jī)票這樣一個(gè)行為,其實(shí)是對(duì)上百個(gè)查詢服務(wù)器發(fā)起了一個(gè)查詢,這個(gè)查詢會(huì)被發(fā)送到多個(gè)微服務(wù)系統(tǒng),這些微服務(wù)系統(tǒng)分別用來(lái)處理航班信息、查詢有無(wú)艙位、機(jī)票價(jià)格、機(jī)場(chǎng)關(guān)鍵字匹配,查找圖片資源等等。每個(gè)子系統(tǒng)的查詢最終聚合得到的結(jié)果,會(huì)匯總到搜索結(jié)果頁(yè)上。

用戶一次查詢,做了這么一個(gè)“全局搜索”。任何一個(gè)子系統(tǒng)變慢,都會(huì)導(dǎo)致最終的搜索變得慢,那用戶體驗(yàn)就會(huì)很差了。

看到這里,你可能會(huì)想,體驗(yàn)差我們做搜索優(yōu)化不就好了么?確實(shí)如此,但一般情況,一個(gè)前端或者后端工程師雖然知道系統(tǒng)查詢耗時(shí),但是他無(wú)從知曉這個(gè)問(wèn)題到底是由哪個(gè)服務(wù)調(diào)用造成的,或者為什么這個(gè)調(diào)用性能差強(qiáng)人意。

首先,這個(gè)工程師可能無(wú)法準(zhǔn)確定位到這次全局搜索是調(diào)用了哪些服務(wù),因?yàn)樾碌姆?wù)、乃至服務(wù)上的某個(gè)功能,都有可能在任何時(shí)間上過(guò)線或修改過(guò)。其次,你不能苛求這個(gè)工程師對(duì)所有參與這次全局搜索的服務(wù)都了如指掌,每一個(gè)服務(wù)都有可能是由不同的團(tuán)隊(duì)開發(fā)或維護(hù)的。再次,搜索服務(wù)還同時(shí)還被其他客戶端使用,比如手機(jī)端,這次全局搜索的性能問(wèn)題甚至有可能是由其他應(yīng)用造成的。

這是過(guò)去我的團(tuán)隊(duì)面臨的問(wèn)題,微服務(wù)讓我們的工程師觀察用戶行為,定位服務(wù)故障很棘手。

—2—什么是分布式鏈路追蹤

剛才說(shuō)的情況,我們迫切需要一些新工具,幫我們理解微服務(wù)分布式系統(tǒng)的行為、精準(zhǔn)分析性能問(wèn)題。于是,分布式系統(tǒng)下鏈路追蹤技術(shù)(Distributed Tracing)出現(xiàn)了。

它的核心思想是:在用戶一次請(qǐng)求服務(wù)的調(diào)?過(guò)程中,無(wú)論請(qǐng)求被分發(fā)到多少個(gè)子系統(tǒng)中,子系統(tǒng)又調(diào)用了更多的子系統(tǒng),我們把系統(tǒng)信息和系統(tǒng)間調(diào)用關(guān)系都追蹤記錄下來(lái)。最終把數(shù)據(jù)集中起來(lái)可視化展示。它會(huì)形成一個(gè)有向圖的鏈路,看起來(lái)像下面這樣。

電商系統(tǒng)的鏈路追蹤圖

后來(lái),鏈路追蹤技術(shù)相關(guān)系統(tǒng)慢慢成熟,涌現(xiàn)了像Dapper、Zipkin、HTrace、OpenTelemetry等優(yōu)秀開源系統(tǒng)。他們也被業(yè)界,特別是互聯(lián)網(wǎng)普遍采用。

目前Dapper(誕生于Google團(tuán)隊(duì))應(yīng)用影響最大,OpenTelemetry已經(jīng)成為最新業(yè)界標(biāo)準(zhǔn),我們重點(diǎn)基于OpenTelemetry講解一下Trace內(nèi)部結(jié)構(gòu)。

—3—鏈路Trace的核心結(jié)構(gòu)

快速入門

我們看看一個(gè)例子,某商家給(顧客)開賬單(要求付款),在系統(tǒng)中大體的流程:

一個(gè)開賬單的例子

當(dāng)商家從client發(fā)起開賬單服務(wù),請(qǐng)求從client程序先后進(jìn)行了一系列操作:

  • 網(wǎng)關(guān)load balancer:client的https請(qǐng)求先經(jīng)過(guò)網(wǎng)關(guān),網(wǎng)關(guān)調(diào)用下面的子系統(tǒng)
  • 身份認(rèn)證auth:網(wǎng)關(guān)RPC請(qǐng)求到服務(wù)auth,auth發(fā)起身份認(rèn)證,完成后通知網(wǎng)關(guān)程序
  • 生成賬單billing:身份認(rèn)證成功后,網(wǎng)關(guān)再發(fā)送RPC請(qǐng)求到服務(wù)billing,billing是生成賬單的操作,billing處理完通知網(wǎng)關(guān)下一步
  • 資源加載resource:billing成功,網(wǎng)關(guān)發(fā)送一個(gè)HTTP請(qǐng)求到服務(wù)resource,加載和賬單相關(guān)資源,比如靜態(tài)圖片,相關(guān)顧客資料,resource完成通知網(wǎng)關(guān)
  • 最后網(wǎng)關(guān)程序處理完開賬單服務(wù),返回結(jié)果給client

例子中,我們把開賬單服務(wù)一個(gè)流程或者叫一個(gè)事務(wù)稱為Trace。這里面有幾個(gè)操作,分別是請(qǐng)求網(wǎng)關(guān)、身份認(rèn)證、生成賬單、加載資源,我們把每個(gè)操作(Operation)稱為一個(gè) Span。

Trace數(shù)據(jù)模型

我們看看Trace廣義的定義:Trace是多個(gè) Span 組成的一個(gè)有向無(wú)環(huán)圖(DAG),每一個(gè) Span 代表 Trace 中被命名并計(jì)時(shí)的連續(xù)性的執(zhí)行片段。我們一般用這樣數(shù)據(jù)模型描述Trace和Span關(guān)系:

[Span user click] ←←←(the root Span) | [Span gateway] | ------ ---------- ----------------------- | | | [Span auth] [Span billing] [Span loading resource]

開賬單Trace數(shù)據(jù)模型

數(shù)據(jù)模型包含了Span之間關(guān)系。Span定義了父級(jí)Span,子Span的概念。一個(gè)父級(jí)的Span會(huì)并行或者串行啟動(dòng)多個(gè)子Span。圖三,Gateway就是auth、billing的父級(jí)Span。

上面這種圖對(duì)于看清各組件的組合關(guān)系是很有用的,但是,它不能很好顯示組件的調(diào)用時(shí)間,是串行調(diào)用還是并行調(diào)用。另外,這種圖也無(wú)法顯示服務(wù)調(diào)用的時(shí)間和先后順序。因此,在鏈路追蹤系統(tǒng)會(huì)用另一種圖展現(xiàn)一個(gè)典型的Trace過(guò)程,如下面所示:

這種展現(xiàn)方式增加顯示了執(zhí)行時(shí)間的上下文,相關(guān)服務(wù)間的層次關(guān)系,任務(wù)的串行或并行調(diào)用關(guān)系。這樣的視圖有助于發(fā)現(xiàn)系統(tǒng)調(diào)用的關(guān)鍵路徑。通過(guò)關(guān)注關(guān)鍵路徑的執(zhí)行過(guò)程,項(xiàng)目團(tuán)隊(duì)可能專注于優(yōu)化路徑中的關(guān)鍵位置,最大幅度的提升系統(tǒng)性能。例如:可以通過(guò)追蹤一個(gè)用戶請(qǐng)求訪問(wèn)鏈路,觀察底層的子系統(tǒng)調(diào)用情況,發(fā)現(xiàn)哪些操作有耗時(shí)重要關(guān)注優(yōu)化。

Span基本結(jié)構(gòu)

前面提到Span通俗概念:一個(gè)操作,它代表系統(tǒng)中一個(gè)邏輯運(yùn)行單元。Span之間通過(guò)嵌套或者順序排列建立因果關(guān)系。Span包含以下對(duì)象:

  • 操作名稱Name:這個(gè)名稱簡(jiǎn)單,并具有可讀性高。例如:一個(gè)RPC方法的名稱,一個(gè)函數(shù)名,或者一個(gè)大型計(jì)算過(guò)程中的子任務(wù)或階段
  • 起始時(shí)間和結(jié)束時(shí)間:操作的生命周期
  • 屬性Attributes:一組鍵值對(duì)構(gòu)成的集合。值可以是字符串、布爾或者數(shù)字類型,一些鏈路追蹤系統(tǒng)也稱為Tags
  • 事件Event
  • 上下文 SpanContext:Span上下文內(nèi)容
  • 鏈接Links:描述Span節(jié)點(diǎn)關(guān)系的連線,它的描述信息保存在SpanContext中

可觀測(cè)平臺(tái)下的Span

屬性Attributes:

我們分析一個(gè)Trace,通過(guò)Span里鍵值對(duì)形式的Attributes獲取基本信息。為了統(tǒng)一約定,Span提供了基礎(chǔ)的Attributes。比如,Span有下面常用的Attributes屬性:

  • 網(wǎng)絡(luò)連接Attributes:這些Attributes用在網(wǎng)絡(luò)操作相關(guān)
  • 線程Attributes

這些Attributes記錄了啟動(dòng)一個(gè)Span后相關(guān)線程信息??紤]到系統(tǒng)可以是不同開發(fā)語(yǔ)言,相應(yīng)還會(huì)記錄相關(guān)語(yǔ)言平臺(tái)信息。下面是不同語(yǔ)言開發(fā)的平臺(tái)獲取線程Id、Name方法:

記錄線程信息,對(duì)于我們排查問(wèn)題時(shí)候非常必要的,當(dāng)出現(xiàn)一個(gè)程序異常,我們至少要知道它什么語(yǔ)言開發(fā),找到對(duì)于研發(fā)工程師。研發(fā)工程師往往需要線程相關(guān)信息快速定位錯(cuò)誤棧。

Span間關(guān)系描述Links:

我們看看之前Span數(shù)據(jù)模型:

[Span gateway] | ------ ------ ------------------ | | | [Span auth] [Span billing] [Span loading resource]

一個(gè)Trace有向無(wú)環(huán)圖,Span是圖的節(jié)點(diǎn),鏈接就是節(jié)點(diǎn)間的連線??梢钥吹揭粋€(gè)Span節(jié)點(diǎn)可以有多個(gè)Link,這代表它有多個(gè)子Span。

Trace定義了Span間兩種基本關(guān)系:

  • ChildOf:Span A是Span B的孩子,即“ChildOf”關(guān)系
  • FollowsFrom:Span A是Span B的父級(jí)Span

Span上下文信息SpanContext:

字面理解Span上下文對(duì)象。它作用就是在一個(gè)Trace中,把當(dāng)前Span和Trace相關(guān)信息傳遞到下級(jí)Span。它的基本結(jié)構(gòu)類似 ,每個(gè)SpanContext包含以下基本屬性:

  • TraceId:隨機(jī)16字節(jié)數(shù)組。比如:4bf92f3577b34da6a3ce929d0e0e4736
  • SpanId:隨機(jī)8字節(jié)數(shù)組。比如:00f067aa0ba902b7
  • Baggage Items是存在于Trace一個(gè)鍵值對(duì)集合,也需要Span間傳輸。

Trace鏈路傳遞初探

在一個(gè)鏈路追蹤過(guò)程中,我們一般會(huì)有多個(gè)Span操作,為了把調(diào)用鏈狀態(tài)在Span中傳遞下去,期望最終保存下來(lái),比如打入日志、保存到數(shù)據(jù)庫(kù)。SpanContext會(huì)封裝一個(gè)鍵值對(duì)集合,然后將數(shù)據(jù)像行李一樣打包,這個(gè)打包的行李OpenTelemetry稱為Baggage(背包)。

Baggage會(huì)在一條追蹤鏈路上的所有Span內(nèi)全局傳輸。在這種情況下,Baggage會(huì)隨著整個(gè)鏈路一同傳播。我們可以通過(guò)Baggage實(shí)現(xiàn)強(qiáng)大的追蹤功能。

方便理解,我們用開賬單服務(wù)演示Baggage效果:

首先,我們?cè)贚oadBalancer請(qǐng)求中加一個(gè)Baggage,LoadBalancer請(qǐng)求了source服務(wù)。

@GetMapping("/loadBalancer")@ResponseBodypublic String loadBalancer(String tag){ Span span = Span.current(); //保存 Baggage Baggage.current() .toBuilder() .put("app.username" "蔣志偉") .build() .makeCurrent();...... ##請(qǐng)求 resourcehttpTemplate.getForEntity(APIURL "/resource" String.class).getBody();

然后我們從resource服務(wù)中獲取Baggage信息,并把它存儲(chǔ)到Span的Attributes中。

@GetMapping("/resource")@ResponseBodypublic String resource(){ String baggage = Baggage.current().getEntryValue("app.username"); Span spanCur = Span.current(); ##獲取當(dāng)前的 Span,把 Baggage 寫的 resource spanCur.setAttribute("app.username" "baggage 傳遞過(guò)來(lái)的 value: " baggage);

最終,我們從跟蹤系統(tǒng)的鏈路UI中點(diǎn)擊source這個(gè)Span,找到傳遞的Baggage信息。

展示Baggage的傳遞

當(dāng)然,Baggage擁有強(qiáng)大功能,也會(huì)有很大的消耗。由于Baggage的全局傳輸,每個(gè)鍵值都會(huì)被拷貝到每一個(gè)本地(local)及遠(yuǎn)程的子Span,如果包含的數(shù)量量太大,或者元素太多,它將降低系統(tǒng)的吞吐量或增加RPC的延遲。

鏈路添加業(yè)務(wù)監(jiān)控

我們進(jìn)行系統(tǒng)鏈路追蹤,除了Trace本身自帶信息,如果我們還希望添加自己關(guān)注的監(jiān)控。Trace支持用打標(biāo)簽Tags方式來(lái)實(shí)現(xiàn)。Tags本質(zhì)還是Span的 Attributes(在OpenTelemetry 定義中,統(tǒng)稱Attributes。在Prometheus、Jaeger里面沿襲Tags老的叫法)。

打Tags的過(guò)程,其實(shí)就是在Span添加我們自定義的Attributes信息,這些Tags大部分和我們業(yè)務(wù)息息相關(guān),為了更方便做業(yè)務(wù)監(jiān)控、分析業(yè)務(wù)。

我們看一個(gè)Java打Tags的例子:頁(yè)面定義好了一個(gè)Tag,名字叫“username”。我們輸入Tags的值,然后把Tags通過(guò)一個(gè)HTTP請(qǐng)求發(fā)送給付賬單的API。

打Tags的演示

API獲取Tags后,把它保存到當(dāng)前Span的Attribute中。這個(gè)Span對(duì)應(yīng)的是代碼里面的一個(gè)Gateway方法,如果不重名Span名稱,默認(rèn)使用Gateway作為Span名稱。

@GetMapping("/loadBalancer")public String gateway(String tag){ Span Span = Span.current(); ##獲取當(dāng)前 Span,添加 username 的 tag Span.setAttribute("username" tag); ...... }

打了Tags后,我們可以在跟蹤系統(tǒng)搜索Tag關(guān)鍵字。通過(guò)Tag可以快速找到對(duì)應(yīng)的Trace。

基于Tags 的搜索

可以看到,根據(jù)Tags的key我們可以很方便篩選想要的Span信息。實(shí)際場(chǎng)景里,我們面臨是從成千上萬(wàn)鏈路中快速定位訪問(wèn)異常的請(qǐng)求。打Tags對(duì)我們?cè)\斷程序非常有幫助。

Baggage和Span Tags的區(qū)別:

  • Baggage在全局范圍內(nèi),(伴隨業(yè)務(wù)系統(tǒng)的調(diào)用)在所有Span間傳輸數(shù)據(jù)。Tags不會(huì)進(jìn)行傳輸,因?yàn)樗麄儾粫?huì)被子級(jí)的 Span 繼承。
  • Span的Tags可以用來(lái)記錄業(yè)務(wù)相關(guān)的數(shù)據(jù),并存儲(chǔ)于追蹤系統(tǒng)中。

全鏈路兼容性考慮

在不同的平臺(tái)、不同的開發(fā)語(yǔ)言、不同的部署環(huán)境(容器非容器)下,為了保證底層追蹤系統(tǒng)實(shí)現(xiàn)兼容性,將監(jiān)控?cái)?shù)據(jù)記錄到一個(gè)可插拔的Tracer上。在絕大部分通用的應(yīng)用場(chǎng)景下,追蹤系統(tǒng)考慮使用某些高度共識(shí)的鍵值對(duì),從而對(duì)診斷應(yīng)用系統(tǒng)更有兼容,通用性。

這個(gè)共識(shí)稱為語(yǔ)義約定Semantic conventions。

你會(huì)從下面一些語(yǔ)義約定看出Trace做了哪些兼容性。

  • General:通用約定,之前提到網(wǎng)絡(luò)連接Attributes,線程Attributes
  • HTTP:針對(duì)HTTP請(qǐng)求的數(shù)據(jù)約定
  • Database:數(shù)據(jù)庫(kù)數(shù)據(jù)約定,包括SQL和NoSQL不同類型
  • RPC/RMI:有關(guān)遠(yuǎn)程調(diào)用的約定
  • Messaging:有關(guān)消息系統(tǒng)的Span約定,例如MQ的訂閱、發(fā)布等
  • Exceptions:針對(duì)異常,錯(cuò)誤狀態(tài)等

例如,我們?cè)L問(wèn)HTTP的應(yīng)用服務(wù)器。應(yīng)用系統(tǒng)處理請(qǐng)求中的URL、IP、HTTP動(dòng)作(get/post等)、返回碼,對(duì)于應(yīng)用系統(tǒng)的診斷是非常有幫助的。監(jiān)控者可以選擇HTTP約定參數(shù)記錄系統(tǒng)狀態(tài),像下面Trace展示的結(jié)果。

Trace默認(rèn)的語(yǔ)義約定

—4—鏈路數(shù)據(jù)如何傳播

在Trace傳遞中有一個(gè)核心的概念,叫Carrier(搬運(yùn)工具)。它表示“搬運(yùn)”Span中SpanContext的工具。比方說(shuō)Trace為了把Span信息傳遞下去,在HTTP調(diào)用場(chǎng)景中,會(huì)有HttpCarrier,在RPC的調(diào)用場(chǎng)景中會(huì)有RpcCarrier來(lái)搬運(yùn)SpanContext。Trace通過(guò)Carrier可以把鏈路追蹤狀態(tài)從一個(gè)進(jìn)程“搬運(yùn)”到另一個(gè)進(jìn)程里。

數(shù)據(jù)傳播基本操作

為了更清晰看懂?dāng)?shù)據(jù)傳播的過(guò)程,我們先了解Span在傳播中有的基本操作:

1、StartSpan:Trace在具體操作中自動(dòng)生成一個(gè)Span

2、Inject注入:將Span的SpanContext寫入到Carrier的過(guò)程

鏈路數(shù)據(jù)為了進(jìn)行網(wǎng)絡(luò)傳輸,需要數(shù)據(jù)進(jìn)行序列化和反序列化。這個(gè)過(guò)程Trace通過(guò)一個(gè)負(fù)責(zé)數(shù)據(jù)序列化反序列化上下文的Formatter接口實(shí)現(xiàn)的。例如在HttpCarrier使用中通常就會(huì)有一個(gè)對(duì)應(yīng)的HttpFormatter。所以Inject注入是委托給Formatter將SpanContext進(jìn)行序列化寫入Carrier。

Formatter提供不同場(chǎng)景序列化的數(shù)據(jù)格式,叫做Format描述。比如:

  • Text Map:基于字符串的Map記錄SpanContext信息,適用RPC網(wǎng)絡(luò)傳輸
  • HTTP Headers:方便解析HTTP Headers信息,用于HTTP傳輸

一個(gè)Python程序?qū)崿F(xiàn)Inject注入過(guò)程,F(xiàn)ormatter序列化SpanContext成Text Map格式。

##Trace 生成一個(gè)span tracer = Tracer() span = tracer.start_span(operation_name='test') tracer.inject( span_context=span.context format=Format.TEXT_MAP carrier=carrier)

3、Extract提取:將SpanContext從Carrier中Extract(提取出來(lái))。

span_ctx = tracer.extract(format=Format.TEXT_MAP carrier={})

同理,從Carrier提取的過(guò)程也需要委托Formatter將SpanContext反序列化。

運(yùn)行原理

鏈路數(shù)據(jù)在HTTP傳遞

我們基于HTTP通信解釋傳播原理。由圖一,這個(gè)過(guò)程大致分為兩步:

1、發(fā)送端將 SpanContext 注入到請(qǐng)求中,相應(yīng)偽代碼實(shí)現(xiàn)

/**** 將 SpanContext 中的 TraceId,SpanId,Baggage 等根據(jù) format 參數(shù)注入到請(qǐng)求中(Carrier)** carrier := opentracing.HTTPHeadersCarrier(httpReq.Header)** err := Tracer.Inject(Span.Context() openTracing.HTTPHeaders carrier)**/Inject(sm SpanContext format interface{} carrier interface{}) error

2、接收端從請(qǐng)求中解析出SpanContext,相應(yīng)偽代碼實(shí)現(xiàn)

// Inject() takes the `sm` SpanContext instance and injects it for// propagation within `carrier`. The actual type of `carrier` depends on/** 根據(jù) format 參數(shù)從請(qǐng)求(Carrier)中解析出 SpanContext(包括 TraceId、SpanId、baggage)。** 例如: ** carrier := opentracing.HTTPHeadersCarrier(httpReq.Header)** clientContext err := Tracer.Extract(opentracing.HTTPHeaders carrier)**/Extract(format interface{} carrier interface{}) (SpanContext error)

Carrier負(fù)責(zé)將追蹤狀態(tài)從一個(gè)進(jìn)程“Carry”(搬運(yùn))到另一個(gè)進(jìn)程。對(duì)于一個(gè)Carrier,如果已經(jīng)被Injected,那么它也可以被Extracted(提?。?,從而得到一個(gè)SpanContext實(shí)例。這個(gè)SpanContext代表著被Injected到Carrier的信息。

說(shuō)到這里,你可能想知道這個(gè)Carrier在HTTP中具體在哪。其實(shí)它就保存到HTTP的Headers中。而且,W3C組織為HTTP支持鏈路追蹤專門在Headers中定義了Trace標(biāo)準(zhǔn):

https://www.w3.org/TR/trace-context/#trace-context-http-headers-format

W3C組織是對(duì)網(wǎng)絡(luò)標(biāo)準(zhǔn)制定的一個(gè)非盈利組織,W3C是萬(wàn)維網(wǎng)聯(lián)盟的縮寫,像HTML、XHTML、CSS、XML的標(biāo)準(zhǔn)就是由W3C來(lái)定制。

跨進(jìn)程間傳播數(shù)據(jù)

數(shù)據(jù)傳播按照?qǐng)鼍胺譃閮深悾哼M(jìn)程內(nèi)傳播、跨進(jìn)程間傳播Cross-Process-Tracing。

進(jìn)程內(nèi)傳播是指Trace在一個(gè)服務(wù)內(nèi)部傳遞,監(jiān)控了服務(wù)內(nèi)部相互調(diào)用情況,相當(dāng)比較簡(jiǎn)單。追蹤系統(tǒng)最困難的部分就是在分布式的應(yīng)用環(huán)境下保持追蹤的正常工作。任何一個(gè)追蹤系統(tǒng),都需要理解多個(gè)跨進(jìn)程調(diào)用間的因果關(guān)系,無(wú)論他們是通過(guò)RPC框架、發(fā)布-訂閱機(jī)制、通用消息隊(duì)列、HTTP請(qǐng)求調(diào)用、UDP傳輸或者其他傳輸模式。所以業(yè)界談起Tracing技術(shù) 往往說(shuō)的是跨進(jìn)程間的分布式鏈路追蹤(Distrubute Tracing)。

我們用OpenTelemetry實(shí)踐一個(gè)HTTP通信的Trace例子:

這是一個(gè)本地Localhost的Java程序,我們向下游服務(wù)192.168.0.100發(fā)起一個(gè)HTTP請(qǐng)求。

程序中使用OpenTelemetry的inject注入,通過(guò)HTTP Headers把Localhost的Trace傳遞給192.168.0.100的下游服務(wù)。傳播前,手動(dòng)還創(chuàng)建兩個(gè)想要一塊傳播的Attributes。

@GetMapping("/contextR")@ResponseBodypublic String contextR() { TextMapSetter setter = new TextMapSetter() { @Override public void set(HttpURLConnection carrier String key String value) { // 我們把上下文放到 HTTP 的 Header carrier.setRequestProperty(key value); } }; Span spanCur = Span.current(); Span outGoing = tracer.spanBuilder("/resource").setSpanKind(SpanKind.CLIENT).startSpan(); try { URL url = new URL("http://192.168.0.100:8080/resource"); HttpURLConnection transportLayer = (HttpURLConnection) url.openConnection(); outGoing.setAttribute("http.method" "GET"); outGoing.setAttribute("http.url" url.toString()); // 將當(dāng)前 Span 的上下文注入到這個(gè) HTTP 請(qǐng)求中 OpenTelemetry.getPropagators().getTextMapPropagator().inject(Context.current() transportLayer setter); // Make outgoing call...

運(yùn)行程序,從監(jiān)控平臺(tái)我們看到,Trace從本地的程序成功傳遞到了192.168.0.100。

跨進(jìn)程傳播數(shù)據(jù)

—5—手動(dòng)控制Trace:自動(dòng)構(gòu)建

上面我們提到Trace,都是鏈路追蹤系統(tǒng)自動(dòng)完成的。雖然這很通用,但在實(shí)際應(yīng)用中,我們有些時(shí)候還想查看更多的跟蹤細(xì)節(jié)和添加業(yè)務(wù)監(jiān)控。鏈路追蹤技術(shù)支持應(yīng)用程序開發(fā)人員手工方式在跟蹤的過(guò)程中添加額外的信息,甚至手動(dòng)啟動(dòng)Span,以期待監(jiān)控更高級(jí)別的系統(tǒng)行為,或幫助調(diào)試問(wèn)題。

OpenTelemetry支持以SDK和API方式手動(dòng)構(gòu)建Trace。API、SDK都可以做一些基本Trace操作,可以理解API是Min實(shí)現(xiàn),SDK是API的超集。生產(chǎn)環(huán)境根據(jù)實(shí)際場(chǎng)景選擇用哪一個(gè)。

手動(dòng)構(gòu)建Trace原理圖

創(chuàng)建Span

要?jiǎng)?chuàng)建Span,只需指定Span的名稱。手動(dòng)創(chuàng)建Span需要顯式結(jié)束操作,它的開始和結(jié)束時(shí)間由鏈路追蹤系統(tǒng)自動(dòng)計(jì)算。Java代碼實(shí)例:

Span Span = Tracer.SpanBuilder("手工創(chuàng)建 SpanOne").startSpan();try{......} finally { Span.end(); //手動(dòng)創(chuàng)建 Span,我們需要手動(dòng)結(jié)束 Span}

應(yīng)用程序運(yùn)行時(shí),我們可以這樣獲取一個(gè)Span。

Span Span = Span.current()

創(chuàng)建帶鏈接Span

一個(gè)Span可以連接一個(gè)或多個(gè)因果相關(guān)的其他Span。實(shí)例中我們創(chuàng)建一個(gè)Span。

叫做“手工創(chuàng)建SpanOne”,然后分別創(chuàng)建了三個(gè)Span,通過(guò)link把它們關(guān)聯(lián)成孩子Span。最后又創(chuàng)建了一個(gè)Span “childThree-Child”,把它作為“childThree”的孩子Span 關(guān)聯(lián):

@GetMapping("/createSpanAndLink")public String createSpanAndLink() { String SpanName = "手工創(chuàng)建 SpanOne"; //創(chuàng)建一個(gè) Span,然后創(chuàng)建三個(gè) child Span,最后關(guān)聯(lián) Span Span SpanOne = Tracer.SpanBuilder(SpanName) .startSpan(); Span childSpan = Tracer.SpanBuilder("childOne") .addLink(SpanOne.getSpanContext()).startSpan(); Span childSpan2 = Tracer.SpanBuilder("childTwo") .addLink(SpanOne.getSpanContext()).startSpan(); Span childSpan3 = Tracer.SpanBuilder("childThree") .addLink(SpanOne.getSpanContext()).startSpan(); //創(chuàng)建一個(gè) Span,關(guān)聯(lián) childSpan3 作為它的 childSpan Span childSpan3Child = Tracer.SpanBuilder("childThree-Child") .addLink(childSpan3.getSpanContext()).startSpan();}

我們看看運(yùn)行程序后,收集的Trace的效果:Link將各個(gè)Span連接起來(lái)。

鏈路UI展示Trace中Span關(guān)系

創(chuàng)建帶事件的Span

Span可以攜帶零個(gè)或多個(gè)Span屬性的命名事件進(jìn)行注釋,每一個(gè)事件都是一個(gè) key:value鍵值對(duì),并自動(dòng)攜帶相應(yīng)的時(shí)間戳。時(shí)間戳表示事件的持續(xù)時(shí)間。

@GetMapping("/event")public String event(){ Span span = Span.current(); span.updateName("創(chuàng)建 eventDemo"); //手動(dòng)更新 Event 持續(xù)時(shí)間 span.addEvent("timeEvent" System.currentTimeMillis() 2000 TimeUnit.MILLISECONDS); //給 Event 添加相關(guān)信息 Attributes appInfo = Attributes.of(AttributeKey .stringKey("app.id") "123456" AttributeKey.stringKey("app.name") "應(yīng)用程序 demo"); span.addEvent("auth.appinfo" appInfo); logger.info("this is a event"); }

在上面程序可以看到,我們還可以給事件手動(dòng)添加時(shí)間戳,這在復(fù)雜系統(tǒng)環(huán)境下還原真實(shí)持續(xù)事件很有意義的??纯催\(yùn)行程序后,追蹤平臺(tái)下Span生成的的效果:

—6—數(shù)據(jù)如何上報(bào)

有了程序的Trace數(shù)據(jù),包含TraceId、Span、Spancontext一系列數(shù)據(jù)。接下來(lái)需要上報(bào)到監(jiān)控系統(tǒng),通過(guò)視圖方式展示出Trace整個(gè)全貌。我們習(xí)慣把Trace數(shù)據(jù)上報(bào)到監(jiān)控過(guò)程稱為數(shù)據(jù)收集(Data Collection)。看看數(shù)據(jù)收集基本原理:

從圖中,看到鏈路收集過(guò)程中,數(shù)據(jù)上報(bào)核心的幾個(gè)組件:

  • Collector:數(shù)據(jù)收集的處理器,它同時(shí)解析、加工監(jiān)控?cái)?shù)據(jù),目的是給監(jiān)控展示平臺(tái)更直觀的視圖,便捷的查詢。
  • Exporters:采集器,從哪個(gè)應(yīng)用程序獲得Trace數(shù)據(jù),目前主流跟蹤系統(tǒng)幾乎可以支持任何語(yǔ)言平臺(tái)下開發(fā)程序,并且兼容主流操作系統(tǒng)和容器。為了支持Trace標(biāo)準(zhǔn)化到這種程序,工程師可謂嘔心瀝血,中間過(guò)程極不容易。

Data Collection在數(shù)據(jù)采集時(shí)候,Collector和Exporters有兩種實(shí)現(xiàn):Agent和Gateway。限于篇幅,我們?cè)诤竺嫖恼略敿?xì)給大家講解。

總結(jié)

今天講解了鏈路追蹤的數(shù)據(jù)傳播、數(shù)據(jù)上報(bào)的原理。如果你想動(dòng)手實(shí)踐文章中的代碼 GitHub開放了地址:https://github.com/laziobird/opentelemetry-jaeger

如果大家喜歡,后面會(huì)介紹鏈路追蹤理論發(fā)展起來(lái)應(yīng)用性能監(jiān)控系統(tǒng)APM。

APM為我們線上應(yīng)用程序在故障定位、性能調(diào)優(yōu)起到巨大作用。同時(shí),我還會(huì)基于APM談?wù)剬?shí)際項(xiàng)目中,比如容器(Kubernetes)、服務(wù)網(wǎng)格(Istio)環(huán)境下,Trace數(shù)據(jù)采集具體實(shí)現(xiàn)和如何做監(jiān)控。

思考

一個(gè)常見(jiàn)的場(chǎng)景:用戶訪問(wèn)一個(gè)每天上百萬(wàn)UV的電商系統(tǒng),現(xiàn)在這個(gè)用戶告訴我們下單不了,系統(tǒng)也沒(méi)有錯(cuò)誤提示。如果作為研發(fā)系統(tǒng)的工程師,你有哪些思路去排查故障呢?

如果用我們提到的鏈路追蹤。能否知道該用戶訪問(wèn)行為,快速定位到哪個(gè)服務(wù)出問(wèn)題,或者你平時(shí)的經(jīng)驗(yàn),是怎樣來(lái)快速解決這樣類似的問(wèn)題?你可以在留言區(qū)與我交流。

本站部分文章來(lái)自網(wǎng)絡(luò)或用戶投稿。涉及到的言論觀點(diǎn)不代表本站立場(chǎng)。閱讀前請(qǐng)查看【免責(zé)聲明】發(fā)布者:方應(yīng),如若本篇文章侵犯了原著者的合法權(quán)益,可聯(lián)系我們進(jìn)行處理。本文鏈接:http://www.gdyuanyu.cn/tougao/89687.html

221381