欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

.NET?Core分布式鏈路追蹤框架的基本實(shí)現(xiàn)原理

 更新時(shí)間:2022年03月30日 11:31:42   作者:癡者工良  
這篇文章介紹了.NET?Core分布式鏈路追蹤框架的基本實(shí)現(xiàn)原理,對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧

分布式追蹤

什么是分布式追蹤

分布式系統(tǒng)

當(dāng)我們使用 Google 或者 百度搜索時(shí),查詢服務(wù)會(huì)將關(guān)鍵字分發(fā)到多臺(tái)查詢服務(wù)器,每臺(tái)服務(wù)器在自己的索引范圍內(nèi)進(jìn)行搜索,搜索引擎可以在短時(shí)間內(nèi)獲得大量準(zhǔn)確的搜索結(jié)果;同時(shí),根據(jù)關(guān)鍵字,廣告子系統(tǒng)會(huì)推送合適的相關(guān)廣告,還會(huì)從競(jìng)價(jià)排名子系統(tǒng)獲得網(wǎng)站權(quán)重。通常一個(gè)搜索可能需要成千上萬(wàn)臺(tái)服務(wù)器參與,需要經(jīng)過(guò)許多不同的系統(tǒng)提供服務(wù)。

多臺(tái)計(jì)算機(jī)通過(guò)網(wǎng)絡(luò)組成了一個(gè)龐大的系統(tǒng),這個(gè)系統(tǒng)即是分布式系統(tǒng)。

在微服務(wù)或者云原生開發(fā)中,一般認(rèn)為分布式系統(tǒng)是通過(guò)各種中間件/服務(wù)網(wǎng)格連接的,這些中間件提供了共享資源、功能(API等)、文件等,使得整個(gè)網(wǎng)絡(luò)可以當(dāng)作一臺(tái)計(jì)算機(jī)進(jìn)行工作。

分布式追蹤

在分布式系統(tǒng)中,用戶的一個(gè)請(qǐng)求會(huì)被分發(fā)到多個(gè)子系統(tǒng)中,被不同的服務(wù)處理,最后將結(jié)果返回給用戶。用戶發(fā)出請(qǐng)求和獲得結(jié)果這段時(shí)間是一個(gè)請(qǐng)求周期。

當(dāng)我們購(gòu)物時(shí),只需要一個(gè)很簡(jiǎn)單的過(guò)程:

獲取優(yōu)惠劵 -> 下單 -> 付款 -> 等待收貨

然而在后臺(tái)系統(tǒng)中,每一個(gè)環(huán)節(jié)都需要經(jīng)過(guò)多個(gè)子系統(tǒng)進(jìn)行協(xié)作,并且有嚴(yán)格的流程。例如在下單時(shí),需要檢查是否有優(yōu)惠卷、優(yōu)惠劵能不能用于當(dāng)前商品、當(dāng)前訂單是否符合使用優(yōu)惠劵條件等。

下圖是一個(gè)用戶請(qǐng)求后,系統(tǒng)處理請(qǐng)求的流程。

圖中出現(xiàn)了很多箭頭,這些箭頭指向了下一步要流經(jīng)的服務(wù)/子系統(tǒng),這些箭頭組成了鏈路網(wǎng)絡(luò)。

在一個(gè)復(fù)雜的分布式系統(tǒng)中,任何子系統(tǒng)出現(xiàn)性能不佳的情況,都會(huì)影響整個(gè)請(qǐng)求周期。根據(jù)上圖,我們?cè)O(shè)想:

1.系統(tǒng)中有可能每天都在增加新服務(wù)或刪除舊服務(wù),也可能進(jìn)行升級(jí),當(dāng)系統(tǒng)出現(xiàn)錯(cuò)誤,我們?nèi)绾味ㄎ粏栴}?

2.當(dāng)用戶請(qǐng)求時(shí),響應(yīng)緩慢,怎么定位問題?

3.服務(wù)可能由不同的編程語(yǔ)言開發(fā),1、2 定位問題的方式,是否適合所有編程語(yǔ)言?

分布式追蹤有什么用呢

隨著微服務(wù)和云原生開發(fā)的興起,越來(lái)越多應(yīng)用基于分布式進(jìn)行開發(fā),但是大型應(yīng)用拆分為微服務(wù)后,服務(wù)之間的依賴和調(diào)用變得越來(lái)越復(fù)雜,這些服務(wù)是不同團(tuán)隊(duì)、使用不同語(yǔ)言開發(fā)的,部署在不同機(jī)器上,他們之間提供的接口可能不同(gRPC、Restful api等)。

為了維護(hù)這些服務(wù),軟件領(lǐng)域出現(xiàn)了 Observability 思想,在這個(gè)思想中,對(duì)微服務(wù)的維護(hù)分為三個(gè)部分:

  • 度量(Metrics):用于監(jiān)控和報(bào)警;
  • 分布式追蹤(Tracing):用于記錄系統(tǒng)中所有的跟蹤信息;
  • 日志(Logging):記錄每個(gè)服務(wù)只能中離散的信息;

這三部分并不是獨(dú)立開來(lái)的,例如 Metrics 可以監(jiān)控 Tracing 、Logging 服務(wù)是否正常運(yùn)行。Tacing 和 Metrics 服務(wù)在運(yùn)行過(guò)程中會(huì)產(chǎn)生日志。

近年來(lái),出現(xiàn)了 APM 系統(tǒng),APM 稱為 應(yīng)用程序性能管理系統(tǒng),可以進(jìn)行 軟件性能監(jiān)視和性能分析。APM 是一種 Metrics,但是現(xiàn)在有融合 Tracing 的趨勢(shì)。

回歸正題,分布式追蹤系統(tǒng)(Tracing)有什么用呢?這里可以以 Jaeger 舉例,它可以:

  • 分布式跟蹤信息傳遞
  • 分布式事務(wù)監(jiān)控
  • 服務(wù)依賴性分析
  • 展示跨進(jìn)程調(diào)用鏈
  • 定位問題
  • 性能優(yōu)化

Jaeger 需要結(jié)合后端進(jìn)行結(jié)果分析,jaeger 有個(gè) Jaeger UI,但是功能并不多,因此還需要依賴 Metrics 框架從結(jié)果呈現(xiàn)中可視化,以及自定義監(jiān)控、告警規(guī)則,所以很自然 Metrics 可能會(huì)把 Tracing 的事情也做了。

Dapper

Dapper 是 Google 內(nèi)部使用的分布式鏈路追蹤系統(tǒng),并沒有開源。

Dapper 用戶接口:

分布式追蹤系統(tǒng)的實(shí)現(xiàn)

下圖是一個(gè)由用戶 X 請(qǐng)求發(fā)起的,穿過(guò)多個(gè)服務(wù)的分布式系統(tǒng),A、B、C、D、E 表示不同的子系統(tǒng)或處理過(guò)程。

在這個(gè)圖中, A 是前端,B、C 是中間層、D、E 是 C 的后端。這些子系統(tǒng)通過(guò) rpc 協(xié)議連接,例如 gRPC。

一個(gè)簡(jiǎn)單實(shí)用的分布式鏈路追蹤系統(tǒng)的實(shí)現(xiàn),就是對(duì)服務(wù)器上每一次請(qǐng)求以及響應(yīng)收集跟蹤標(biāo)識(shí)符(message identifiers)和時(shí)間戳(timestamped events)。

分布式服務(wù)的跟蹤系統(tǒng)需要記錄在一次特定的請(qǐng)求后系統(tǒng)中完成的所有工作的信息。用戶請(qǐng)求可以是并行的,同一時(shí)間可能有大量的動(dòng)作要處理,一個(gè)請(qǐng)求也會(huì)經(jīng)過(guò)系統(tǒng)中的多個(gè)服務(wù),系統(tǒng)中時(shí)時(shí)刻刻都在產(chǎn)生各種跟蹤信息,必須將一個(gè)請(qǐng)求在不同服務(wù)中產(chǎn)生的追蹤信息關(guān)聯(lián)起來(lái)。

為了將所有記錄條目與一個(gè)給定的發(fā)起者X關(guān)聯(lián)上并記錄所有信息,現(xiàn)在有兩種解決方案,黑盒(black-box)和基于標(biāo)注(annotation-based)的監(jiān)控方案。

黑盒方案:

假定需要跟蹤的除了上述信息之外沒有額外的信息,這樣使用統(tǒng)計(jì)回歸技術(shù)來(lái)推斷兩者之間的關(guān)系。

基于標(biāo)注的方案:

依賴于應(yīng)用程序或中間件明確地標(biāo)記一個(gè)全局ID,從而連接每一條記錄和發(fā)起者的請(qǐng)求。

優(yōu)缺點(diǎn):

雖然黑盒方案比標(biāo)注方案更輕便,他們需要更多的數(shù)據(jù),以獲得足夠的精度,因?yàn)樗麄円蕾囉诮y(tǒng)計(jì)推論?;跇?biāo)注的方案最主要的缺點(diǎn)是,很明顯,需要代碼植入。在我們的生產(chǎn)環(huán)境中,因?yàn)樗械膽?yīng)用程序都使用相同的線程模型,控制流和 RPC 系統(tǒng),我們發(fā)現(xiàn),可以把代碼植入限制在一個(gè)很小的通用組件庫(kù)中,從而實(shí)現(xiàn)了監(jiān)測(cè)系統(tǒng)的應(yīng)用對(duì)開發(fā)人員是有效地透明。

Dapper 基于標(biāo)注的方案,接下來(lái)我們將介紹 Dapper 中的一些概念知識(shí)。

跟蹤樹和 span

從形式上看,Dapper 跟蹤模型使用的是樹形結(jié)構(gòu),Span 以及 Annotation。

在前面的圖片中,我們可以看到,整個(gè)請(qǐng)求網(wǎng)絡(luò)是一個(gè)樹形結(jié)構(gòu),用戶請(qǐng)求是樹的根節(jié)點(diǎn)。在 Dapper 的跟蹤樹結(jié)構(gòu)中,樹節(jié)點(diǎn)是整個(gè)架構(gòu)的基本單元。

span 稱為跨度,一個(gè)節(jié)點(diǎn)在收到請(qǐng)求以及完成請(qǐng)求的過(guò)程是一個(gè) span,span 記錄了在這個(gè)過(guò)程中產(chǎn)生的各種信息。每個(gè)節(jié)點(diǎn)處理每個(gè)請(qǐng)求時(shí)都會(huì)生成一個(gè)獨(dú)一無(wú)二的的 span id,當(dāng) A -> C -> D 時(shí),多個(gè)連續(xù)的 span 會(huì)產(chǎn)生父子關(guān)系,那么一個(gè) span 除了保存自己的 span id,也需要關(guān)聯(lián)父、子 span id。生成 span id 必須是高性能的,并且能夠明確表示時(shí)間順序,這點(diǎn)在后面介紹 Jaeger 時(shí)會(huì)介紹。

Annotation 譯為注釋,在一個(gè) span 中,可以為 span 添加更多的跟蹤細(xì)節(jié),這些額外的信息可以幫助我們監(jiān)控系統(tǒng)的行為或者幫助調(diào)試問題。Annotation 可以添加任意內(nèi)容。

到此為止,簡(jiǎn)單介紹了一些分布式追蹤以及 Dapper 的知識(shí),但是這些不足以嚴(yán)謹(jǐn)?shù)恼f(shuō)明分布式追蹤的知識(shí)和概念,建議讀者有空時(shí)閱讀 Dapper 論文。

要實(shí)現(xiàn) Dapper,還需要代碼埋點(diǎn)、采樣、跟蹤收集等,這里就不再細(xì)談了,后面會(huì)介紹到,讀者也可以看看論文。

Jaeger 和 OpenTracing

OpenTracing

OpenTracing 是與分布式系統(tǒng)無(wú)關(guān)的API和用于分布式跟蹤的工具,它不僅提供了統(tǒng)一標(biāo)準(zhǔn)的 API,還致力于各種工具,幫助開發(fā)者或服務(wù)提供者開發(fā)程序。

OpenTracing 為標(biāo)準(zhǔn) API 提供了接入 SDK,支持這些語(yǔ)言:Go, JavaScript, Java, Python, Ruby, PHP, Objective-C, C++, C#。

當(dāng)然,我們也可以自行根據(jù)通訊協(xié)議,自己封裝 SDK。

接下來(lái)我們要一點(diǎn)點(diǎn)弄清楚 OpenTracing 中的一些概念和知識(shí)點(diǎn)。由于 jaeger 是 OpenTracing 最好的實(shí)現(xiàn),因此后面講 Jaeger 就是 Opentracing ,不需要將兩者嚴(yán)格區(qū)分。

Jaeger 結(jié)構(gòu)

首先是 JAEGER 部分,這部分是代碼埋點(diǎn)等流程,在分布式系統(tǒng)中處理,當(dāng)一個(gè)跟蹤完成后,通過(guò) jaeger-agent 將數(shù)據(jù)推送到 jaeger-collector。jaeger-collector 負(fù)責(zé)處理四面八方推送來(lái)的跟蹤信息,然后存儲(chǔ)到后端,可以存儲(chǔ)到 ES、數(shù)據(jù)庫(kù)等。Jaeger-UI 可以將讓用戶在界面上看到這些被分析出來(lái)的跟蹤信息。

OpenTracing API 被封裝成編程語(yǔ)言的 SDK(jaeger-client),例如在 C# 中是 .dll ,Java 是 .jar,應(yīng)用程序代碼通過(guò)調(diào)用 API 實(shí)現(xiàn)代碼埋點(diǎn)。

jaeger-Agent 是一個(gè)監(jiān)聽在 UDP 端口上接收 span 數(shù)據(jù)的網(wǎng)絡(luò)守護(hù)進(jìn)程,它會(huì)將數(shù)據(jù)批量發(fā)送給 collector。

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

在 OpenTracing 中,跟蹤信息被分為 Trace、Span 兩個(gè)核心,它們按照一定的結(jié)構(gòu)存儲(chǔ)跟蹤信息,所以它們是 OpenTracing 中數(shù)據(jù)模型的核心。

Trace 是一次完整的跟蹤,Trace 由多個(gè) Span 組成。下圖是一個(gè) Trace 示例,由 8 個(gè) Span 組成。

Tracing:

a Trace can be thought of as a directed acyclic graph (DAG) of Spans。

有點(diǎn)難翻譯,大概意思是 Trace 是多個(gè) Span 組成的有向非循環(huán)圖。

在上面的示例中,一個(gè) Trace 經(jīng)過(guò)了 8 個(gè)服務(wù),A -> C -> F -> G 是有嚴(yán)格順序的,但是從時(shí)間上來(lái)看,B 、C 是可以并行的。為了準(zhǔn)確表示這些 Span 在時(shí)間上的關(guān)系,我們可以用下圖表示:

有個(gè)要注意的地方, 并不是 A -> C -> F 表示 A 執(zhí)行結(jié)束,然后 C 開始執(zhí)行,而是 A 執(zhí)行過(guò)程中,依賴 C,而 C 依賴 F。因此,當(dāng) A 依賴 C 的過(guò)程完成后,最終回到 A 繼續(xù)執(zhí)行。所以上圖中 A 的跨度最大。

Span 格式

要深入學(xué)習(xí),就必須先了解 Span,請(qǐng)讀者認(rèn)真對(duì)照下面的圖片和 Json:

json 地址:https://github.com/whuanle/DistributedTracing/issues/1

后續(xù)將圍繞這張圖片和 Json 來(lái)舉例講述 Span 相關(guān)知識(shí)。

Trace

一個(gè)簡(jiǎn)化的 Trace 如下:

注:不同編程語(yǔ)言的字段名稱有所差異,gRPC 和 Restful API 的格式也有所差異。

            "traceID": "790e003e22209ca4",
            "spans":[...],
            "processes":{...}

前面說(shuō)到,在 OpenTracing 中,Trace 是一個(gè)有向非循環(huán)圖,那么 Trace 必定有且只有一個(gè)起點(diǎn)。

這個(gè)起點(diǎn)會(huì)創(chuàng)建一個(gè) Trace 對(duì)象,這個(gè)對(duì)象一開始初始化了 trace id 和 process,trace id 是一個(gè) 32 個(gè)長(zhǎng)度的字符串組成,它是一個(gè)時(shí)間戳,而 process 是起點(diǎn)進(jìn)程所在主機(jī)的信息。

下面筆者來(lái)說(shuō)一些一下 trace id 是怎么生成的。trace id 是 32個(gè)字符串組成,而實(shí)際上只使用了 16 個(gè),因此,下面請(qǐng)以 16 個(gè)字符長(zhǎng)度去理解這個(gè)過(guò)程。

首先獲取當(dāng)前時(shí)間戳,例如獲得 1611467737781059 共 16 個(gè)數(shù)字,單位是微秒,表示時(shí)間 2021-01-24 13:55:37,秒以下的單位這里就不給出了,明白表示時(shí)間就行。

在 C# 中,將當(dāng)前時(shí)間轉(zhuǎn)為這種時(shí)間戳的代碼:

        public static long ToTimestamp(DateTime dateTime)
        {
            DateTime dt1970 = new DateTime(1970, 1, 1, 0, 0, 0, 0);
            return (dateTime.Ticks - dt1970.Ticks)/10;
        }

// 結(jié)果:1611467737781059

如果我們直接使用 Guid 生成或者 string 存儲(chǔ),都會(huì)消耗一些性能和內(nèi)存,而使用 long,剛剛好可以表示時(shí)間戳,還可以節(jié)約內(nèi)存。

獲得這個(gè)時(shí)間戳后,要傳輸?shù)?Jaeger Collector,要轉(zhuǎn)為 byet 數(shù)據(jù),為什么要這樣不太清楚,按照要求傳輸就是了。

將 long 轉(zhuǎn)為一個(gè) byte 數(shù)組:

            var bytes = BitConverter.GetBytes(time);
			// 大小端
            if (BitConverter.IsLittleEndian)
            {
                Array.Reverse(bytes);
            }

long 占 8 個(gè)字節(jié),每個(gè) byte 值如下:

0x00 0x05 0xb9 0x9f 0x12 0x13 0xd3 0x43

然后傳輸?shù)?Jaeger Collector 中,那么獲得的是一串二進(jìn)制,怎么表示為字符串的 trace id?

可以先還原成 long,然后將 long 輸出為 16 進(jìn)制的字符串:

轉(zhuǎn)為字符串(這是C#):

Console.WriteLine(time.ToString("x016"));

結(jié)果:

0005b99f1213d343

Span id 也是這樣轉(zhuǎn)的,每個(gè) id 因?yàn)榕c時(shí)間戳相關(guān),所以在時(shí)間上是唯一的,生成的字符串也是唯一的。

這就是 trace 中的 trace id 了,而 trace process 是發(fā)起請(qǐng)求的機(jī)器的信息,用 Key-Value 的形式存儲(chǔ)信息,其格式如下:

                        {
                            "key": "hostname",
                            "type": "string",
                            "value": "Your-PC"
                        },
                        {
                            "key": "ip",
                            "type": "string",
                            "value": "172.6.6.6"
                        },
                        {
                            "key": "jaeger.version",
                            "type": "string",
                            "value": "CSharp-0.4.2.0"
                        }

Ttace 中的 trace id 和 process 這里說(shuō)完了,接下來(lái)說(shuō) trace 的 span。

Span

Span 由以下信息組成:

  • An operation name:操作名稱,必有;
  • A start timestamp:開始時(shí)間戳,必有;
  • A finish timestamp:結(jié)束時(shí)間戳,必有;
  • Span Tags.:Key-Value 形式表示請(qǐng)求的標(biāo)簽,可選;
  • Span Logs:Key-Value 形式表示,記錄簡(jiǎn)單的、結(jié)構(gòu)化的日志,必須是字符串類型,可選;
  • SpanContext :跨度上下文,在不同的 span 中傳遞,建立關(guān)系;
  • Referencest:引用的其它 Span;

span 之間如果是父子關(guān)系,則可以使用 SpanContext 綁定這種關(guān)系。父子關(guān)系有 ChildOfFollowsFrom 兩種表示,ChildOf 表示 父 Span 在一定程度上依賴子 Span,而 FollowsFrom 表示父 Span 完全不依賴其子Span 的結(jié)果。

一個(gè) Span 的簡(jiǎn)化信息如下(不用理會(huì)字段名稱大小寫):

{
                    "traceID": "790e003e22209ca4",
                    "spanID": "4b73f8e8e77fe9dc",
                    "flags": 1,
                    "operationName": "print-hello",
                    "references": [],
                    "startTime": 1611318628515966,
                    "duration": 259,
                    "tags": [
                        {
                            "key": "internal.span.format",
                            "type": "string",
                            "value": "proto"
                        }
                    ],
                    "logs": [
                        {
                            "timestamp": 1611318628516206,
                            "fields": [
                                {
                                    "key": "event",
                                    "type": "string",
                                    "value": "WriteLine"
                                }
                            ]
                        }
                    ]
}

OpenTracing API

在 OpenTracing API 中,有三個(gè)主要對(duì)象:

  • Tracer
  • Span
  • SpanContext

Tracer可以創(chuàng)建Spans并了解如何跨流程邊界對(duì)它們的元數(shù)據(jù)進(jìn)行Inject(序列化)和Extract(反序列化)。它具有以下功能:

  • 開始一個(gè)新的 Span
  • Inject一個(gè)SpanContext到一個(gè)載體
  • Extract一個(gè)SpanContext從載體

由起點(diǎn)進(jìn)程創(chuàng)建一個(gè) Tracer,然后啟動(dòng)進(jìn)程發(fā)起請(qǐng)求,每個(gè)動(dòng)作產(chǎn)生一個(gè) Span,如果有父子關(guān)系,Tracer 可以將它們關(guān)聯(lián)起來(lái)。當(dāng)請(qǐng)求完成后, Tracer 將跟蹤信息推送到 Jaeger-Collector中。

 

SpanContext 是在不同的 Span 中傳遞信息的,SpanContext 包含了簡(jiǎn)單的 Trace id、Span id 等信息。

我們繼續(xù)以下圖作為示例講解。

A 創(chuàng)建一個(gè) Tracer,然后創(chuàng)建一個(gè) Span,代表自己 (A),再創(chuàng)建兩個(gè) Span,分別代表 B、C,然后通過(guò) SpanContext 傳遞一些信息到 B、C;B 和 C 收到 A 的消息后,也創(chuàng)建一個(gè) Tracer ,用來(lái) Tracer.extract(...) ;其中 B 沒有后續(xù),可以直接返回結(jié)果;而 C 的 Tracer 繼續(xù)創(chuàng)建兩個(gè) Span,往 D、E 傳遞 SpanContext。

這個(gè)過(guò)程比較復(fù)雜,筆者講不好,建議讀者參與 OpenTracing 的官方文檔。

詳細(xì)的 OpenTracing API,可以通過(guò)編程語(yǔ)言編寫相應(yīng)服務(wù)時(shí),去學(xué)習(xí)各種 API 的使用。

到此這篇關(guān)于.NET Core分布式鏈路追蹤框架的基本實(shí)現(xiàn)原理的文章就介紹到這了。希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。

相關(guān)文章

最新評(píng)論