自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

Sentry 監(jiān)控 - 面向全棧開發(fā)人員的分布式跟蹤

安全 應(yīng)用安全 分布式
在 Web 的早期,編寫 Web 應(yīng)用程序很簡單。開發(fā)人員使用 PHP 等語言在服務(wù)器上生成 HTML,與 MySQL 等單一關(guān)系數(shù)據(jù)庫進行通信,大多數(shù)交互性由靜態(tài) HTML 表單組件驅(qū)動。雖然調(diào)試工具很原始,但理解代碼的執(zhí)行流程很簡單。

[[427214]]

歡迎來到我們關(guān)于全棧開發(fā)人員分布式跟蹤(Distributed Tracing)的系列的第 1 部分。在本系列中,我們將學習分布式跟蹤的細節(jié),以及它如何幫助您監(jiān)控全棧應(yīng)用程序日益復雜的需求。

在 Web 的早期,編寫 Web 應(yīng)用程序很簡單。開發(fā)人員使用 PHP 等語言在服務(wù)器上生成 HTML,與 MySQL 等單一關(guān)系數(shù)據(jù)庫進行通信,大多數(shù)交互性由靜態(tài) HTML 表單組件驅(qū)動。雖然調(diào)試工具很原始,但理解代碼的執(zhí)行流程很簡單。

在今天的現(xiàn)代 web 棧中,它什么都不是。全棧開發(fā)人員需要編寫在瀏覽器中執(zhí)行的 JavaScript,與多種數(shù)據(jù)庫技術(shù)互操作,并在不同的服務(wù)器架構(gòu)(例如:serverless)上部署服務(wù)器端代碼。如果沒有合適的工具,了解瀏覽器中的用戶交互如何關(guān)聯(lián)到服務(wù)器堆棧深處的 500 server error 幾乎是不可能的。Enter:分布式跟蹤。

我試圖解釋 2021 年我的 web 堆棧中的瓶頸。

分布式跟蹤(Distributed tracing)是一種監(jiān)控技術(shù),它將多個服務(wù)之間發(fā)生的操作和請求聯(lián)系起來。這允許開發(fā)人員在端到端請求從一個服務(wù)移動到另一個服務(wù)時“跟蹤(trace)”它的路徑,讓他們能夠查明對整個系統(tǒng)產(chǎn)生負面影響的單個服務(wù)中的錯誤或性能瓶頸。

在這篇文章中,我們將了解有關(guān)分布式跟蹤概念的更多信息,在代碼中查看端到端(end-to-end)跟蹤示例,并了解如何使用跟蹤元數(shù)據(jù)為您的日志記錄和監(jiān)控工具添加有價值的上下文。完成后,您不僅會了解分布式跟蹤的基礎(chǔ)知識,還會了解如何應(yīng)用跟蹤技術(shù)來更有效地調(diào)試全棧 Web 應(yīng)用程序。

但首先,讓我們回到開頭:什么是分布式追蹤?

分布式追蹤基礎(chǔ)

分布式跟蹤是一種記錄多個服務(wù)的連接操作的方法。通常,這些操作是由從一個服務(wù)到另一個服務(wù)的請求發(fā)起的,其中“請求(request)”可以是實際的 HTTP 請求,也可以是通過任務(wù)隊列或其他一些異步方式調(diào)用的工作。

跟蹤由兩個基本組件組成:

  • Span 描述發(fā)生在服務(wù)上的操作或 “work”。Span 可以描述廣泛的操作——例如,響應(yīng) HTTP 請求的 web 服務(wù)器的操作——也可以描述單個函數(shù)的調(diào)用。
  • trace 描述了一個或多個連接 span 的端到端(end-to-end)旅程。如果 trace 連接在多個服務(wù)上執(zhí)行的 span(“work”),則該 trace 被認為是分布式跟蹤。

讓我們看一個假設(shè)的分布式跟蹤示例。

上圖說明了 trace 如何從一個服務(wù)(一個在瀏覽器上運行的 React 應(yīng)用程序)開始,并通過調(diào)用 API Web Server 繼續(xù),甚至進一步調(diào)用后臺任務(wù) worker。此圖中的 span 是在每個服務(wù)中執(zhí)行的 work,每個 span 都可以“追溯到(traced)”由瀏覽器應(yīng)用程序啟動的初始工作(initial work)。最后,由于這些操作發(fā)生在不同的服務(wù)上,因此該跟蹤被認為是分布式的。

描述廣泛操作的跨度(例如:響應(yīng) HTTP request 的 Web server 的完整生命周期)有時被稱為事務(wù)跨度(transaction spans),甚至只是事務(wù)。我們將在本系列的第 2 部分中更多地討論事務(wù)與跨度(transactions vs. spans)。

跟蹤和跨度標識符

到目前為止,我們已經(jīng)確定了跟蹤的組件,但我們還沒有描述這些組件是如何鏈接在一起的。

首先,每個跟蹤都用跟蹤標識符(trace identifier)唯一標識。這是通過在根跨度(root span)中創(chuàng)建一個唯一的隨機生成值(即 UUID)來完成的——這是啟動整個跟蹤的初始操作。在我們上面的示例中,根跨度出現(xiàn)在瀏覽器應(yīng)用程序中。

其次,每個 span 首先需要被唯一標識。這通過在跨度開始其操作時創(chuàng)建唯一的跨度標識符(或 span_id)來完成。這個 span_id 創(chuàng)建應(yīng)該發(fā)生在 trace 內(nèi)發(fā)生的每個 span(或操作)處進行。

讓我們重新審視我們假設(shè)的跟蹤示例。在上圖中,您會注意到跟蹤標識符唯一地標識了跟蹤,并且該跟蹤中的每個跨度也擁有一個唯一的跨度標識符。

然而,生成 trace_id 和 span_id 是不夠的。要實際連接這些服務(wù),您的應(yīng)用程序必須在從一個服務(wù)向另一個服務(wù)發(fā)出請求時傳播所謂的跟蹤上下文(trace context)。

跟蹤上下文

跟蹤上下文(trace context)通常僅由兩個值組成:

  • 跟蹤標識符(或 trace_id):在根跨度中生成的唯一標識符,用于標識整個跟蹤。這與我們在上一節(jié)中介紹的跟蹤標識符相同;它以不變的方式傳播到每個下游服務(wù)。
  • 父標識符(或 parent_id):產(chǎn)生當前操作的“父”跨度的 span_id。

下圖顯示了在一個服務(wù)中啟動的請求如何將跟蹤上下文傳播到下游的下一個服務(wù)。您會注意到 trace_id 保持不變,而 parent_id 在請求之間發(fā)生變化,指向啟動最新操作的父跨度。

有了這兩個值,對于任何給定的操作,就可以確定原始(root)服務(wù),并按照導致當前操作的順序重建所有父/祖先(parent/ancestor)服務(wù)。

工作示例(代碼演示)

示例源碼:

  • https://github.com/getsentry/distributed-tracing-examples

為了更好地理解這一點,讓我們實際實現(xiàn)一個基本的跟蹤實現(xiàn),其中瀏覽器應(yīng)用程序是由跟蹤上下文連接的一系列分布式操作的發(fā)起者。

首先,瀏覽器應(yīng)用程序呈現(xiàn)一個表單:就本示例而言,是一個“邀請用戶(invite user)”表單。表單有一個提交事件處理程序,它在表單提交時觸發(fā)。讓我們將此提交處理程序視為我們的根跨度(root span),這意味著當調(diào)用處理程序時,會生成 trace_id 和 span_id。

接下來,完成一些工作以從表單中收集用戶輸入的值,然后最后向我們的 Web 服務(wù)器發(fā)出一個到 /inviteUser API 端點的 fetch 請求。作為此 fetch 請求的一部分,跟蹤上下文作為兩個自定義 HTTP header 傳遞:trace-id 和 parent-id(即當前 span 的 span_id)。

  1. // browser app (JavaScript) 
  2. import uuid from 'uuid'
  3.  
  4. const traceId = uuid.v4(); 
  5. const spanId = uuid.v4(); 
  6.  
  7. console.log('Initiate inviteUser POST request', `traceId: ${traceId}`); 
  8.  
  9. fetch('/api/v1/inviteUser?email=' + encodeURIComponent(email), { 
  10.    method: 'POST'
  11.    headers: { 
  12.        'trace-id': traceId, 
  13.        'parent-id': spanId, 
  14.    } 
  15. }).then((data) => { 
  16.    console.log('Success!'); 
  17. }).catch((err) => { 
  18.    console.log('Something bad happened', `traceId: ${traceId}`); 
  19. }); 

請注意,這些是用于說明目的的非標準 HTTP header。作為 W3C traceparent 規(guī)范的一部分,正在積極努力標準化 tracing HTTP header,該規(guī)范仍處于 “Recommendation” 階段。

  • https://www.w3.org/TR/trace-context/

在接收端,API web server 處理請求并從 HTTP 請求中提取跟蹤元數(shù)據(jù)(tracing metadata)。然后它會排隊一個 job 以向用戶發(fā)送電子郵件,并將跟蹤上下文作為 job 描述中“meta”字段的一部分附加。最后,它返回一個帶有 200 狀態(tài) code 的響應(yīng),表明該方法成功。

請注意,雖然服務(wù)器返回了成功的響應(yīng),但實際的“工作”直到后臺任務(wù) worker 拿起新排隊的 job 并實際發(fā)送電子郵件后才完成。

在某個點上,隊列處理器開始處理排隊的電子郵件作業(yè)。再一次,跟蹤(trace)和父標識符(parent identifier)被提取出來,就像它們在 web server 中的早些時候一樣。

  1. // API Web Server 
  2. const Queue = require('bull'); 
  3. const emailQueue = new Queue('email'); 
  4. const uuid = require('uuid'); 
  5.  
  6. app.post("/api/v1/inviteUser", (req, res) => { 
  7.   const spanId = uuid.v4(), 
  8.     traceId = req.headers["trace-id"], 
  9.     parentId = req.headers["parent-id"]; 
  10.  
  11.   console.log( 
  12.     "Adding job to email queue"
  13.     `[traceId: ${traceId},`, 
  14.     `parentId: ${parentId},`, 
  15.     `spanId: ${spanId}]` 
  16.   ); 
  17.  
  18.   emailQueue.add({ 
  19.     title: "Welcome to our product"
  20.     to: req.params.email, 
  21.     meta: { 
  22.       traceId: traceId, 
  23.  
  24.       // the downstream span's parent_id is this span's span_id 
  25.       parentId: spanId, 
  26.     }, 
  27.   }); 
  28.  
  29.   res.status(200).send("ok"); 
  30. }); 
  31.  
  32. // Background Task Worker 
  33. emailQueue.process((job, done) => { 
  34.   const spanId = uuid.v4(); 
  35.   const { traceId, parentId } = job.data.meta; 
  36.  
  37.   console.log( 
  38.     "Sending email"
  39.     `[traceId: ${traceId},`, 
  40.     `parentId: ${parentId},`, 
  41.     `spanId: ${spanId}]` 
  42.   ); 
  43.  
  44.   // actually send the email 
  45.   // ... 
  46.  
  47.   done(); 
  48. }); 

分布式系統(tǒng) Logging

您會注意到,在我們示例的每個階段,都會使用 console.log 進行 logging 調(diào)用,該調(diào)用還發(fā)出當前 trace、span 和 parent 標識符。在完美的同步世界中——每個服務(wù)都可以登錄到同一個集中式 logging 工具——這些日志語句中的每一個都會依次出現(xiàn):

如果在這些操作過程中發(fā)生異?;蝈e誤行為,使用這些或額外的日志語句來查明來源將相對簡單。但不幸的現(xiàn)實是,這些都是分布式服務(wù),這意味著:

Web 服務(wù)器通常處理許多并發(fā)請求。Web 服務(wù)器可能正在執(zhí)行歸因于其他請求的工作(并發(fā)出日志記錄語句)。

網(wǎng)絡(luò)延遲會影響操作順序。從上游服務(wù)發(fā)出的請求可能不會按照它們被觸發(fā)的順序到達目的地。

后臺 worker 可能有排隊的 job。在到達此跟蹤中排隊的確切 job 之前,worker 可能必須先完成先前排隊的 job。

在一個更現(xiàn)實的例子中,我們的日志調(diào)用可能看起來像這樣,它反映了同時發(fā)生的多個操作:

如果不跟蹤 metadata,就不可能了解哪個動作調(diào)用哪個動作的拓撲結(jié)構(gòu)。但是通過在每次 logging 調(diào)用時發(fā)出跟蹤 meta 信息,可以通過過濾 traceId 快速過濾跟蹤中的所有 logging 調(diào)用,并通過檢查 spanId 和 parentId 關(guān)系重建確切的順序。

這就是分布式跟蹤的威力:通過附加描述當前操作(span id)、產(chǎn)生它的父操作(parent id)和跟蹤標識符(trace id)的元數(shù)據(jù),我們可以增加日志記錄和遙測數(shù)據(jù)以更好地理解 分布式服務(wù)中發(fā)生的事件的確切順序。

在真實的分布式跟蹤環(huán)境中

在本文的過程中,我們一直在使用一個有點人為的示例。在真正的分布式跟蹤環(huán)境中,您不會手動生成和傳遞所有的跨度和跟蹤標識符。您也不會依賴 console.log(或其他日志記錄)調(diào)用來自己發(fā)出跟蹤元數(shù)據(jù)。您將使用適當?shù)母檸靵頌槟幚頇z測和發(fā)送跟蹤數(shù)據(jù)。

OpenTelemetry

OpenTelemetry 是一組開源工具、API 和 SDK,用于檢測、生成和導出正在運行的軟件中的遙測數(shù)據(jù)。它為大多數(shù)流行的編程語言提供了特定于語言的實現(xiàn),包括瀏覽器 JavaScript 和 Node.js。

  • https://opentelemetry.io/
  • https://github.com/open-telemetry/opentelemetry-js

Sentry

Sentry 以多種方式使用這種遙測。例如,Sentry 的性能監(jiān)控功能集使用跟蹤數(shù)據(jù)生成瀑布圖,說明跟蹤中分布式服務(wù)操作的端到端延遲。

 

Sentry 還使用跟蹤元數(shù)據(jù)來增強它的錯誤監(jiān)控功能,以了解在一個服務(wù)(如服務(wù)器后端)中觸發(fā)的錯誤如何傳播到另一個服務(wù)(如前端)中的錯誤。

 

責任編輯:武曉燕 來源: 黑客下午茶
相關(guān)推薦

2021-09-30 23:12:52

監(jiān)控分布式跟蹤

2019-09-05 19:56:23

開發(fā)編程程序

2022-01-13 23:15:29

Docker開發(fā)嵌入式

2022-03-05 23:09:52

開發(fā)軟件工程師前端

2022-11-02 14:43:29

2022-04-20 10:56:06

JavaJVM參數(shù)

2023-03-02 17:44:30

DevOps開發(fā)

2022-06-06 10:30:23

容器鏡像

2024-02-21 08:00:00

機器學習Java大語言模型

2015-02-10 09:24:04

Web開發(fā)JavaScript工具

2019-08-27 14:21:44

Python 開發(fā)程序員

2016-10-18 10:45:00

開發(fā)開源

2014-08-05 11:06:29

開發(fā)工具安卓工具

2015-06-23 09:24:13

編程社區(qū)開發(fā)人員

2016-11-09 13:46:00

云數(shù)據(jù)庫

2018-04-09 09:00:00

Java測試框架庫JUnit

2019-01-28 08:00:00

Node.JSWeb框架前端

2010-08-09 16:09:25

2016-10-14 19:30:21

云計算云數(shù)據(jù)庫數(shù)據(jù)庫

2021-09-27 09:00:00

開發(fā)微服務(wù)架構(gòu)
點贊
收藏

51CTO技術(shù)棧公眾號