从浏览器到 C++:用 RUM 与 OpenTelemetry 接通调用链

Vue、Go、Python、Java、C++ 的 Trace Context 传播、OTLP 上报与链路验证。

云原生可观测

Vue 请求依次经过 Go、Python、Java 和 C++。各服务通过 W3C Trace Context 传递父子关系,通过 OTLP 上报 Span。浏览器请求头、响应中的 Trace ID 与平台调用树用于交叉验证。

配套代码:ARMS-Multilingual-Traceid。

准备五个模块和端口

Shell
git clone https://github.com/Roliyal/CROlordCodelibrary.git
cd CROlordCodelibrary/Chapter6Custom/ARMS-Multilingual-Traceid
cp .env.example .env
chmod 600 .env
模块 本机端口 业务接口
Vue / Vite 5173 页面入口
Go 网关 8080 /api/hello
Python 8081 /py/work
Java 8082 /java/work
C++ 8083 /cpp/work

需要 Node.js 20、Go 1.22 及以上、Python 3.11、JDK 17/Maven 3.9、CMake 3.20 及 vcpkg。各服务在自己的目录启动,后端读取根目录配置。

上下文与父子关系

标识 含义
Trace ID 一次分布式调用共享的标识,通常为 32 位十六进制
Span ID 当前操作的标识,通常为 16 位十六进制
traceparent 传播版本、Trace ID、父 Span ID 和采样标记的 W3C 请求头

同一条调用链中的 Trace ID 应一致,各操作有自己的 Span ID。收到的 traceparent 中的 Span ID 指向上游父操作,不是当前服务新建的 Server Span。

接收请求时提取上下文,创建服务端 Span;调用下游时,在当前上下文下创建客户端 Span,再注入请求头。只有转发 Header、没有正确建立父子关系,也可能留下错误的调用树。

配置 RUM 和浏览器请求头

RUM 的 tracing 按完整 URL 匹配自有接口。以下 Web SDK 配置的采样率范围为 0–100:

JavaScript
tracing: {
  enable: true,
  sample: 100,
  allowedUrls: [{
    match: url => {
      const target = new URL(url, location.href)
      return target.origin === location.origin &&
        target.pathname.startsWith('/api/')
    },
    propagatorTypes: ['tracecontext']
  }]
}

联调时可以全采样,正式运行再按流量与成本调整,并核对所用 SDK 的参数口径。跨域 API 应明确配置自己的目标域名,不要用过宽规则匹配第三方请求。

跨域时,网关或后端需要正确处理 OPTIONS,并在 Access-Control-Allow-Headers 中允许实际使用的 traceparent、tracestate 等请求头。使用 Cookie 时,还需核对允许来源与凭据配置。

各语言接入要点

服务 接入要点
Go 网关 用 otelhttp.NewHandler 处理入站,otelhttp.NewTransport 处理出站,沿请求 Context 调用
Python / Flask Flask instrumentation 提取入站上下文,Requests instrumentation 传播出站上下文
Java 使用框架 instrumentation,或手动 extract、创建 Server Span,再在 Client Span 范围内 inject
C++ 配置 W3C propagator,适配 Header carrier,提取父上下文后创建 Server Span

SDK 的 TracerProvider、Exporter 与 Propagator 应在应用启动时初始化,退出时完成刷新。每个服务使用独立、稳定的 service.name。

同一段 HTTP 调用要明确由自动 instrumentation 还是手动代码负责。自动创建 Span 和注入 Header 已正常工作时,不必再重复包一层客户端 Span,也不需要把“再注入一次”当作默认做法。

异步任务或线程切换也可能丢失上下文。排查时从上一跳发出的 Header 与下一跳收到的 Header 对比,再看新建 Span 的父节点。

配置与启动

当前仓库根目录 .env 里的核心配置如下:

.env
OTEL_EXPORTER_OTLP_ENDPOINT=http://127.0.0.1:4318/v1/traces
GO_PORT=8080
PY_PORT=8081
JAVA_PORT=8082
CPP_PORT=8083
PY_URL=http://127.0.0.1:8081
JAVA_URL=http://127.0.0.1:8082
CPP_URL=http://127.0.0.1:8083
VITE_ARMS_RUM_PID=<你的RUM应用PID>
VITE_ARMS_RUM_ENDPOINT=<你的浏览器上报地址>
VITE_APP_VERSION=trace-demo-001

默认指向本机 Collector。当前示例的手动初始化代码把 OTEL_EXPORTER_OTLP_ENDPOINT 作为完整 traces 地址传给 HTTP Exporter;改为云端时,需要按代码的实际读取方式设置完整地址。当前 Java 示例还没有额外认证 Header 的配置入口,需要认证时先补齐 Exporter 配置,或经服务端 Collector 转发。

各模块在独立终端运行,从示例根目录进入对应目录,按下游到上游的顺序启动:

Shell
# 1. C++:先按仓库清单安装 vcpkg 依赖
cd cpp-svc
cmake -S . -B build -DCMAKE_TOOLCHAIN_FILE="$VCPKG_ROOT/scripts/buildsystems/vcpkg.cmake"
cmake --build build -j
./build/cpp-svc
Shell
# 2. Java:当前仓库使用 JDK HttpServer
cd java-svc
mvn -q -DskipTests package
java -jar target/app.jar
Shell
# 3. Python
cd python-svc
python3 -m venv .venv
source .venv/bin/activate
pip install -r requirements.txt
python app.py
Shell
# 4. Go 网关
cd go-gateway
go run .
Shell
# 5. Vue 页面
cd frontend
npm install
npm run dev

先对 8080–8083 的 /healthz 分别做本机检查,再打开 Vite 输出的页面地址。容器部署时,127.0.0.1 指向各自容器,必须把下游 URL 改为可达服务名。

不同工程的 OTLP 端点规则

业务 Header 负责把调用串起来,OTLP 负责把观测数据发送出去。两条路径都要成功。

后端 OTLP/HTTP 配置示例:

Text
OTEL_SERVICE_NAME=go-gateway
OTEL_EXPORTER_OTLP_PROTOCOL=http/protobuf
OTEL_EXPORTER_OTLP_TRACES_ENDPOINT=https://collector.example.com/v1/traces

地址需要替换为平台生成或实际部署的端点。通用 OTEL_EXPORTER_OTLP_ENDPOINT 与 traces 专用地址的路径处理不同:专用地址通常按完整 URL 使用,不会再自动补 /v1/traces。云平台有自定义路径时,保留其生成值。

同时核对协议、TLS、认证和出网。后端 OTLP 凭据只配置在服务端,不能发给浏览器;RUM 使用自己的浏览器接入配置。

端到端验证

联调环境可暂时在响应中返回当前后端 trace_id,便于对照,但这本身不会建立 Trace 关系。

  1. 打开浏览器 Network,确认 API 请求携带 traceparent。
  2. 在 Go 入口核对收到的上下文和当前 Trace ID。
  3. 逐跳检查 Python、Java、C++,确认 Trace ID 相同,Span 父子关系正确。
  4. 到链路平台查询该 Trace,检查浏览器入口与各服务是否可关联。
  5. 制造一次可控的慢调用或错误,核对对应 Span 的耗时和错误信息。

若所有请求都变成独立根 Trace,先检查传播器与服务端提取;若 Header 和父子关系正确但平台缺少某一段,再检查采样、Exporter、认证和刷新结果。

日志与事件关联

日志从当前 Span 读取 trace_id、span_id,就能从异常日志反查调用链。没有相应索引或平台跳转配置时,需要先手动查询,不能默认接入后就能自动互跳。

前端业务事件可以附带本次请求返回的 Trace ID。不要用全局“最近一次 Trace ID”关联所有事件:页面并发请求时,后返回的结果会覆盖前一个请求,造成串线。

指标适合保留服务、路由、状态等有限维度。Trace ID 不应作为普通指标标签;平台支持 Exemplar 时,再通过采样关联到具体请求。

参考

一丝痕迹

很高兴,在这里遇见你。

头像,随邮箱而来。

填写邮箱后,会自动匹配 Gravatar。