Vue 请求依次经过 Go、Python、Java 和 C++。各服务通过 W3C Trace Context 传递父子关系,通过 OTLP 上报 Span。浏览器请求头、响应中的 Trace ID 与平台调用树用于交叉验证。
配套代码:ARMS-Multilingual-Traceid。
准备五个模块和端口
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:
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 里的核心配置如下:
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 转发。
各模块在独立终端运行,从示例根目录进入对应目录,按下游到上游的顺序启动:
# 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# 2. Java:当前仓库使用 JDK HttpServer
cd java-svc
mvn -q -DskipTests package
java -jar target/app.jar# 3. Python
cd python-svc
python3 -m venv .venv
source .venv/bin/activate
pip install -r requirements.txt
python app.py# 4. Go 网关
cd go-gateway
go run .# 5. Vue 页面
cd frontend
npm install
npm run dev先对 8080–8083 的 /healthz 分别做本机检查,再打开 Vite 输出的页面地址。容器部署时,127.0.0.1 指向各自容器,必须把下游 URL 改为可达服务名。
不同工程的 OTLP 端点规则
业务 Header 负责把调用串起来,OTLP 负责把观测数据发送出去。两条路径都要成功。
后端 OTLP/HTTP 配置示例:
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 关系。
- 打开浏览器 Network,确认 API 请求携带
traceparent。 - 在 Go 入口核对收到的上下文和当前 Trace ID。
- 逐跳检查 Python、Java、C++,确认 Trace ID 相同,Span 父子关系正确。
- 到链路平台查询该 Trace,检查浏览器入口与各服务是否可关联。
- 制造一次可控的慢调用或错误,核对对应 Span 的耗时和错误信息。
若所有请求都变成独立根 Trace,先检查传播器与服务端提取;若 Header 和父子关系正确但平台缺少某一段,再检查采样、Exporter、认证和刷新结果。
日志与事件关联
日志从当前 Span 读取 trace_id、span_id,就能从异常日志反查调用链。没有相应索引或平台跳转配置时,需要先手动查询,不能默认接入后就能自动互跳。
前端业务事件可以附带本次请求返回的 Trace ID。不要用全局“最近一次 Trace ID”关联所有事件:页面并发请求时,后返回的结果会覆盖前一个请求,造成串线。
指标适合保留服务、路由、状态等有限维度。Trace ID 不应作为普通指标标签;平台支持 Exemplar 时,再通过采样关联到具体请求。