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

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

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

分布式追蹤

什么是分布式追蹤

分布式系統(tǒng)

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

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

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

分布式追蹤

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

當(dāng)我們購物時,只需要一個很簡單的過程:

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

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

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

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

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

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

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

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

分布式追蹤有什么用呢

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

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

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

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

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

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

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

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

Dapper

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

Dapper 用戶接口:

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

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

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

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

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

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

黑盒方案:

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

基于標注的方案:

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

優(yōu)缺點:

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

Dapper 基于標注的方案,接下來我們將介紹 Dapper 中的一些概念知識。

跟蹤樹和 span

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

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

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

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

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

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

Jaeger 和 OpenTracing

OpenTracing

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

OpenTracing 為標準 API 提供了接入 SDK,支持這些語言:Go, JavaScript, Java, Python, Ruby, PHP, Objective-C, C++, C#。

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

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

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

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

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

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

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

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

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

Tracing:

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

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

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

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

Span 格式

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

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

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

Trace

一個簡化的 Trace 如下:

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

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

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

這個起點會創(chuàng)建一個 Trace 對象,這個對象一開始初始化了 trace id 和 process,trace id 是一個 32 個長度的字符串組成,它是一個時間戳,而 process 是起點進程所在主機的信息。

下面筆者來說一些一下 trace id 是怎么生成的。trace id 是 32個字符串組成,而實際上只使用了 16 個,因此,下面請以 16 個字符長度去理解這個過程。

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

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

        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 存儲,都會消耗一些性能和內(nèi)存,而使用 long,剛剛好可以表示時間戳,還可以節(jié)約內(nèi)存。

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

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

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

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

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

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

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

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

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

結(jié)果:

0005b99f1213d343

Span id 也是這樣轉(zhuǎn)的,每個 id 因為與時間戳相關(guān),所以在時間上是唯一的,生成的字符串也是唯一的。

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

                        {
                            "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 這里說完了,接下來說 trace 的 span。

Span

Span 由以下信息組成:

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

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

一個 Span 的簡化信息如下(不用理會字段名稱大小寫):

{
                    "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 中,有三個主要對象:

  • Tracer
  • Span
  • SpanContext

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

  • 開始一個新的 Span
  • Inject一個SpanContext到一個載體
  • Extract一個SpanContext從載體

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

 

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

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

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

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

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

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

相關(guān)文章

最新評論