OpenTelemetry WebSocket 監控終極方案:打通最后一公里

概述

OpenTelemetry,以下簡稱 OTEL,是由 CNCF 托管的“一站式可觀測性標準”,把指標、鏈路、日志三大信號統一為單一 SDK/API,零侵入地采集從瀏覽器、移動端到后端、容器、云服務的全棧遙測數據,并支持 40+ 后端一鍵導出,讓分布式系統的黑盒瞬間變透明。

OpenTelemetry-JS 是 OpenTelemetry 開源的 JavaScript/TypeScript 觀測框架,可在瀏覽器與 Node.js 中無侵入地采集 Traces、Metrics、Logs,自動埋點 HTTP、Fetch、WebSocket、gRPC、數據庫等調用鏈,一鍵導出至 Jaeger、Prometheus、Zipkin 等后端,實現前端到后端的統一可觀測性。

本文章主要通過參考 opentelemetry-js 相關開源方案,經過代碼編寫以及前端業務自埋點改造,演示 OTEL 前端 Span 如何上報到觀測云,以及基于 OTEL 的前端 Span 上報,如何實現在 WebSocket 應用場景的最后一公里探測的最佳實踐。

眾所周知,OTEL 的前端和后端都是通用的 Span 數據上報方式,而觀測云又兼容 OTEL 協議并且 DataKit 開箱即用支持 OTEL Span 數據的上報,因此對于 WebSocket 應用,基于 OTEL 的后端與前端 Span 埋點監控可以在鏈路層面實現完整的端到端的監控。

前端 Span 上報觀測云實踐

功能特性

  • OpenTelemetry SDK 初始化
  • 基于 trace parent 創建 span
  • OTLP 協議數據導出
  • 批量 span 處理
  • 分布式追蹤上下文傳播
  • TypeScript 支持

代碼說明

otel-span/
├── index.ts              # 主入口文件
├── create-span.ts        # OpenTelemetry span 創建邏輯
├── package.json          # 項目依賴配置
├── tsconfig.json         # TypeScript 配置
index.ts

index.ts?作為主入口文件

import { setupOTelSDK, createSpanWithTraceParent } from './create-span'// 初始化 OpenTelemetry SDK
setupOTelSDK()// 使用traceparent創建span, 可以在請求 request header中獲取
const traceParent = '00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01'
const spanName = 'test-span'console.log('開始創建 span...')
const span = createSpanWithTraceParent(traceParent, spanName)
console.log('span 創建完成!')// 等待一段時間確保 span 被導出
setTimeout(() => {console.log('程序執行完成')process.exit(0)
}, 2000)

create-span.ts

create-span.ts?用于創建 span 邏輯

import { Resource } from '@opentelemetry/resources'
import { BatchSpanProcessor, WebTracerProvider } from '@opentelemetry/sdk-trace-web'
import { ZoneContextManager } from '@opentelemetry/context-zone'
import { trace, SpanContext, TraceFlags, context } from '@opentelemetry/api'
import { W3CTraceContextPropagator } from '@opentelemetry/core'
import { OTLPTraceExporter } from '@opentelemetry/exporter-trace-otlp-proto'const setupOTelSDK = () => {const resource = Resource.default().merge(new Resource({'service.name': 'test',}))const tracerProvider = new WebTracerProvider({resource: resource,})const traceExporter = new OTLPTraceExporter({url: 'http://127.0.0.1:9529/otel/v1/traces',headers: {},})const spanProcessor = new BatchSpanProcessor(traceExporter, {// 可選配置參數maxExportBatchSize: 100, // 每批最多處理的span數量scheduledDelayMillis: 1000, // 定期導出的間隔時間(毫秒)})// propagation.setGlobalPropagator(new W3CTraceContextPropagator());// 設置上下文傳播器const contextManager = new ZoneContextManager()tracerProvider.addSpanProcessor(spanProcessor)tracerProvider.register({contextManager,propagator: new W3CTraceContextPropagator(),})trace.setGlobalTracerProvider(tracerProvider)}const parseTraceParent = (traceParent: string) => {const parts = traceParent.split('-')if (parts.length !== 4) throw new Error('Invalid trace_parent format')if (parts[0] !== '00') throw new Error('Unsupported trace_parent version')const traceId = parts[1]const parentSpanId = parts[2]if (traceId.length !== 32) throw new Error('traceId must be 32 characters')if (parentSpanId.length !== 16) throw new Error('parentSpanId must be 16 characters')if (!isHex(traceId)) throw new Error('traceId contains invalid hex characters')if (!isHex(parentSpanId)) throw new Error('parentSpanId contains invalid hex characters')return [traceId, parentSpanId]
}const isHex = (s: string) => {return /^[0-9a-fA-F]+$/.test(s)
}const createSpanWithTraceParent = (traceParent: string, spanName: string) => {if (!traceParent) returnconst [traceId, parentSpanId] = parseTraceParent(traceParent)const tracer = trace.getTracer('Browser')// 創建SpanContextconst spanContext: SpanContext = {traceId: traceId,spanId: parentSpanId,traceFlags: TraceFlags.SAMPLED,// traceState: new TraceState(),isRemote: true,}// 包裝SpanContext為Spanconst parentSpan = trace.wrapSpanContext(spanContext)// 創建父級上下文 - 修正這一行const parentContext = trace.setSpan(context.active(), parentSpan)// 創建并啟動子spanconst childSpan = tracer.startSpan(spanName,{// attributes: {//   "parsing time": `${10000/1000}μs`// }},parentContext)try {// console.info(`Child span started with trace_id: ${traceId}`);// 業務邏輯...} finally {childSpan.end()}
}export { setupOTelSDK, createSpanWithTraceParent }

擴展說明

添加自定義屬性

const childSpan = tracer.startSpan(spanName,{attributes: {'custom.attribute': 'value','user.id': '12345','operation.type': 'read'}},parentContext
)

添加事件

childSpan.addEvent('operation.started', {'input.size': inputSize
})

設置狀態

childSpan.setStatus({code: SpanStatusCode.OK,message: 'Operation completed successfully'
})

上報測試

1、克隆或解壓項目

https://github.com/lrwh/observable-demo/tree/main/otel-span

數據上報地址使用觀測云的本地的 DataKit 為例。

2、安裝依賴:npm install

3、開發模式試運行:npm run dev

數據上報服務名為“test”,span 名稱為“test-span” 。

4、觀測云 DataKit 數據接收與展示

WebSocket 應用場景實戰

場景描述

某平臺已實現基于 OTEL 的后端 Span 的上報,前端三端的監控是基于觀測云的 SDK 進行了集成,也實現了一定意義上的前端RUM數據和和后端 OTEL 的鏈路數據關聯,但是 WebSocket 長連接打破了傳統的請求-響應模式,傳統 HTTP 的 Trace 是請求粒度的,而 WebSocket 連接可能持續數小時,而且重點是 WebSocket 的 Server 端也會發起一些業務數據推送請求到客戶端時,此時僅通過后端的 OTEL 鏈路無法確定數據什么時候推送到的客戶端,以及客戶端的渲染情況表現如何。

方案與原理

首先,觀測云在三端客戶端(web,安卓,IOS)通過自身的 SDK 集成,會生成基于 w3c_traceparent 的 Span,相關的 traceparent 上下文會傳遞到 WebSocket Server 后端鏈路 Span,當后端的 WebSocket Server 推業務請求數據到客戶端時,會繼續傳播 traceparent 上下文給 OTEL 前端 Span,進而通過補充 OTEL 前端 Span 的最后一公里的數據上報,實現整個 websocket 通信的全鏈路監控以及鏈路不同階段調用的耗時情況。

前端自埋點

  • 通過前端埋點來探測 WebSocket Server 端什么時間剛好把業務數據請求發送到客戶端

如下圖所示,在前端業務代碼中定義時,后端傳過來 data.traceparent,隨后即執行核心業務代碼創建 span 的操作,即上述“前端 Span 上報觀測云“章節中類似主入口中的index.ts 的 createSpanWithTraceParent 方法。

const span = createSpanWithTraceParent(traceParent, spanName)

也即是最終會調用 create-span.ts 程序文件中的 createSpanWithTraceParent 方法。

  • 通過前端埋點來探測 WebSocket Server 端推送業務請求數據到客戶端之后,客戶端什么時候渲染完成

如下圖,同理,也是 WebSocket Server 端 traceparent 數據傳播到客戶端,即調用 createSpanWithTraceParent(traceParent, spanName) 方法來實現。

更多自埋點類似原理,需要自行選擇合適位置進行埋點。

效果展示

  • 拓撲展示

  • 鏈路展示

總結

基于 OTEL 前端 Span 的數據上報與自定義埋點改造,解決了 WebSocket 應用場景下 WebSocket Server 到客戶端最后一公里的探測問題,從而使 WebSocket 應用的請求通信有了端到端的可觀測,整個通信過程的性能耗細節一覽無余。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/bicheng/93933.shtml
繁體地址,請注明出處:http://hk.pswp.cn/bicheng/93933.shtml
英文地址,請注明出處:http://en.pswp.cn/bicheng/93933.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

VS Code 出現的 Web 視圖加載錯誤和服務工作者注冊失敗問題解決方案

針對 VS Code 或 Cursor (vscode系)中出現的 Web 視圖加載錯誤和服務工作者注冊失敗問題,以下是永久性解決方案的完整操作指南:解決方案步驟打開命令面板 使用快捷鍵 CtrlShiftP(Windows/Linux)或 CmdShift…

【qml-4】qml與c++交互(類型多例)

背景: 【qml-1】qml與c交互第一次嘗試(實例注入) 【qml-2】嘗試一個有模式的qml彈窗 【qml-3】qml與c交互第二次嘗試(類型注冊) 【qml-4】qml與c交互(類型多例) 【qml-5】qml與c交互&#…

圖數據庫如何構筑 Web3 風控防線 聚焦批量注冊與鏈上盜轉 悅數圖數據庫

隨著 Web3 生態的不斷演進,鏈上風險呈現出團伙化、隱蔽化和動態化的趨勢,傳統的單點風控手段已難以應對復雜多變的攻擊模式。尤其在批量注冊薅羊毛與鏈上交易盜轉洗錢等高頻風險場景中,攻擊者往往通過偽造身份、跨鏈操作、多層嵌套轉賬等方式…

恒流源電路學習

恒流源的設計原理: 如圖所示你可以看到右邊的的推到公式得到紅點處的電壓是一個和左邊相關的定值,所以呢右邊的電流就是電壓除以那個4Ω,所以得到右邊的電路的電流大體是一個定值,不管你再加什么東西都可以保持這個電流&#xff…

基于生成對抗網絡的模糊圖像恢復原理與技術實現

1. 引言圖像模糊是數字圖像處理中的常見問題,其成因包括相機抖動、物體運動、聚焦不良等。傳統方法如維納濾波、Lucy-Richardson 算法等依賴于模糊核估計和逆濾波,在復雜場景下性能有限。生成對抗網絡(Generative Adversarial Networks, GAN&…

【Doris 系列】Doris IP 變更修復

FE 恢復 異常日志 查看 fe.out 會有以下報錯,此時 fe 進程是無法啟動的,操作前注意備份所有 fe 的元數據并停止上游讀寫動作! java.io.IOException: the self host 192.168.31.78 does not equal to the host in ROLE file 192.168.31.81. Yo…

安卓14系統應用收不到開機廣播

安卓14系統應用收不到開機廣播 - Wesley’s Blog 前段時間有測試反饋在安卓14 上面某系統應用恢復出廠設置后沒有自啟動,究竟是什么原因呢? 回顧 Android 從3.1開始,會將新安裝并且從未被啟動的應用置為“STOPPED”狀態,或者被…

C# Attribute 方法擴展

場景 剛寫完一個干凈利落的方法,比如保存數據到數據庫,邏輯清晰、結構優雅, 第二天,“嘿,保存完數據,記得給客戶發個郵件哦~” 第三天,“能不能再發個消息通知其他系統?” 第四天&am…

【URP】[法線貼圖]為什么主要是藍色的?

【從UnityURP開始探索游戲渲染】專欄-直達 法線貼圖呈現藍紫色調(尤其以藍色為主)是由其?存儲原理、切線空間坐標系設計及顏色編碼規則共同決定的?。 核心原因:法線向量的存儲規則? ?法線向量的物理范圍? 法線是單位向量,…

驅動開發系列63 - NVIDIA 開源GPU驅動open-gpu-kernel-modules編譯調試

目錄 一:通過apt方式安裝nvidia 驅動 二:通過 .run 方式安裝nvidia驅動 三:編譯安裝nvidia開源內核驅動 四:驗證和調試 五:卸載驅動 1. 以apt方式安裝nvidia 驅動的卸載方法 2. 以.run方式安裝nvidia驅動的卸載方法 六:安裝CUDA環境 一:通過apt方式安裝nvidia 驅動…

對KingbaseES架構的解析:從讀寫分離到異地災備的技術實現與保障機制

聲明:文章為本人真實測評博客,非廣告,并沒有推廣該平臺 ,為用戶體驗文章 本人旨在分享最真實的用戶體驗,為關注此類產品的朋友們提供一個客觀的參考。 文章目錄一、架構全景:四級高可用構建數字基礎1.1 物…

Visual Studio中的常用調試功能(上)

1、利用斷點進行調試添加斷點的方式有以下幾種1.鍵盤快捷鍵F92.通過菜單【Debug(調試)】-》【Toggle BreakPoint(切換斷點)】3.點擊代碼行左邊的空白處(推薦)設置斷點后,按F5運行程序&#xff0…

Linux -- 線程同步

1.1條件變量 (1)當?個線程互斥地訪問某個變量時,它可能發現在其它線程改變狀態之前,它什么也做不了。 (2)例如?個線程訪問隊列時,發現隊列為空,它只能等待,只到其它線程將?個節點添加到隊列 中。這種情況就需要?到…

前端進階指南:JavaScript性能優化實戰全解析

深入剖析 JavaScript 性能瓶頸,分享優化技巧與最佳實踐,讓你的前端應用更快、更穩、更流暢。 📑 目錄 一、前言 二、性能瓶頸的常見來源 三、JavaScript代碼優化技巧 1. 避免重復計算 2. 合理使用防抖與節流 3. 使用事件委托 四、渲染…

RabbitMQ:SpringAMQP Direct Exchange(直連型交換機)

目錄一、案例需求二、基礎配置三、代碼實現直連型交換機也叫做定向交換機,通過RoutingKey綁定交換機與隊列直接的關系。 生產者源碼 消費者源碼 一、案例需求 在RabbitMQ控制臺中,聲明隊列direct.queue1和direct.queue2。在RabbitMQ控制臺中&#xff…

implement libtime on Windows

因為Windows的time命令和Linux的time命令不一樣&#xff0c;嘗試實現libtime libtime.h /** libtime.h - 跨平臺時間測量庫* 功能&#xff1a;執行外部命令并測量其運行時間和資源使用*/#ifndef LIBTIME_H #define LIBTIME_H#include <stdio.h> #include <stdlib.h>…

Unity進階--C#補充知識點--【C#各版本的新功能新語法】C#1~4與C#5

來源于唐老獅的視頻教學&#xff0c;僅作記錄和感悟記錄&#xff0c;方便日后復習或者查找 一.C#版本與Unity的關系 1.各Unity版本支持的C#版本 更多信息可以在Untiy官網說明查看 https://docs.unity3d.com/2020.3/Documentation/Manual/CSharpCompiler.html&#xff08;這個好…

水閘安全綜合監測系統解決方案

一、方案概述 水閘作為重要的水利工程設施&#xff0c;承擔著防洪、排澇、供水和灌溉等關鍵功能。其安全性直接關系到下游人民群眾的生命財產安全以及區域經濟的穩定發展。近年來&#xff0c;隨著極端天氣頻發和工程老化問題日益突出&#xff0c;水閘安全監測工作顯得尤為重要。…

基于單片機智能點滴輸液系統

傳送門 &#x1f449;&#x1f449;&#x1f449;&#x1f449;其他作品題目速選一覽表 &#x1f449;&#x1f449;&#x1f449;&#x1f449;其他作品題目功能速覽 概述 該系統基于單片機控制技術&#xff0c;結合傳感器和無線通信模塊&#xff0c;實現對輸液過程的實…

AI數據倉庫管理提升效率

內容概要在數字化轉型浪潮中&#xff0c;AI數據倉庫管理正重塑企業數據處理格局。本部分簡要介紹其核心機制&#xff0c;即通過智能API接入外部數據源實現多平臺數據無縫整合&#xff0c;隨后應用數據清洗技術去除冗余信息&#xff0c;確保數據質量。同時&#xff0c;加密存儲機…