有奖捉虫:办公协同&微信生态&物联网文档专题 HOT
文档中心 > 前端性能监控 > 最佳实践 > 腾讯云可观测平台全链路接入

实践背景

最近两年可观测性呈流行趋势,对于大多数开发来说,需要一个具备简单接入和一体化的产品以便使用,本文将以  OpenTelemetry 前后端接入为例,简单介绍如何快速改造用户现有的业务,并且通过前后端 trace 打通的案例,来帮助用户解决开发和排障中实际的问题。

实践前提

接入平台选择了 腾讯云可观测平台 
主要使用其中的 应用性能监控 APM 前端性能监控 RUM 两个服务

一. 后端服务接入 OpenTelemetry

对于后端项目接入 OpenTelemetry,每种语言的接入各不相同。具体的接入文档可参见 应用性能监控 APM - 接入文档
针对 Java 语言,可以使用 OpenTelemetry 自动注入代码,收集应用程序的跟踪数据,并将数据发送到指定的后端。使用 OpenTelemetry Java Agent 接入 OpenTelemetry 非常简单,只需下载并运行 Java Agent,然后在应用程序中添加相应的配置即可。
具体来说,可以按照以下步骤进行操作:

步骤1:下载并运行 Java Agent

可参见 OpenTelemetry 接入文档 进行探针下载。下载后,需要将 Java Agent 的 JAR 文件添加到应用程序的启动参数中,例如:
-javaagent: /path/to/opentelemetry-javaagent.jar

步骤2:添加相应的配置

可以使用环境变量、启动参数的形式来指定配置。例如,可以使用以下环境变量来指定要使用的跟踪器和后端服务:
OTEL_RESOURCE_ATTRIBUTES=service.name=<appName>,token=<token>
OTEL_EXPORTER_OTLP_ENDPOINT=<endpoint>
如果使用的是 TKE 集群部署的容器服务,那么还可以通过 为容器服务 TKE 应用安装探针 进行 JavaAgent 的安装,简化安装流程,同时也能够在 APM 控制台上观察到对应 TKE 容器的运行情况。



此外,我们还可以通过在日志中打印 traceID 实现 trace 与 log 的打通,参见 OpenTelemetry 关联日志 进行相关代码的改造,就能在日志中将每个请求产生的 traceID 以及 spanID 打印到日志中,完成二者的打通。同时接入 日志服务,即可自动采集业务日志到腾讯云可观测平台上。



通过配置索引,即可通过 traceID 与 APM 链路进行关联,实现日志与链路数据的打通。这样,就可以更加方便地进行日志分析和故障排查。



使用 OpenTelemetry Java Agent 接入 OpenTelemetry 可以自动注入跟踪代码,无需手动编写和管理跟踪代码。同时,Java Agent 也提供了丰富的配置选项,可以根据需要进行配置。因此,建议使用 OpenTelemetry Java Agent 来接入 OpenTelemetry,以简化跟踪代码的编写和管理。




二. 前端页面接入 OpenTelemetry

对于前端项目来说,改造当前项目,接入 OpenTelemetry 成本并不低,需要开发者接入 ot 的 SDK,对接口进行 hook,还需要理解 tracing 协议,了解数据如何上报等。OpenTelemetry 官方提供了比较丰富的文档(点此进入 OpenTelemetry 前端接口文档入口),但是对于前端项目来说,参见 Demo 接入会更加方便。

步骤1:代码接入

如下以某项目的接入为例,简单介绍代码接入方式:
import { SimpleSpanProcessor } from '@opentelemetry/sdk-trace-base';
import { OTLPTraceExporter } from '@opentelemetry/exporter-trace-otlp-http';
import { WebTracerProvider } from '@opentelemetry/sdk-trace-web';
import { FetchInstrumentation } from '@opentelemetry/instrumentation-fetch';
import { XMLHttpRequestInstrumentation } from '@opentelemetry/instrumentation-xml-http-request';
import { ZoneContextManager } from '@opentelemetry/context-zone';
import { DocumentLoadInstrumentation } from '@opentelemetry/instrumentation-document-load';
import { registerInstrumentations } from '@opentelemetry/instrumentation';
import { Resource } from '@opentelemetry/resources';

const provider = new WebTracerProvider({
resource: new Resource({
'token': 'jqyteBLxxxxxxxxxxBU', // 注意这里需要使用前面 APM 申请的 token
})
});

provider.addSpanProcessor(new SimpleSpanProcessor(new OTLPTraceExporter({
url: '/v1/traces' // 这里是自定义的 trace 上报地址,本 demo 直接在该域名下配置 nginx 转发
})));

provider.register({
contextManager: new ZoneContextManager(),
});

registerInstrumentations({
instrumentations: [
// 上报页面加载耗时
new DocumentLoadInstrumentation(),
// 给 fetch 接口插装,拦截所有的 fetch 请求上报接口相关数据
new FetchInstrumentation(),
// 给 XHR 插装,拦截 XHR 接口并上报相关数据
new XMLHttpRequestInstrumentation({
// 屏蔽 aegis sdk 的接口监控
ignoreUrls: [/aegis.qq.com/, /rumt-zh.com/],
}),
],
});

步骤2:上报服务转发

上述代码中,开发者设置了上报地址为 `/v1/traces`,浏览器会向同域名的 `/v1/traces` 接口上报 trace 数据,并且对于打桩的接口,请求头上会自动带上 traceparent (或者 b3,根据用户使用 OpenTelemetry 协议不同而不同)。
业务接口请求头带上 traceparent



业务上报 trace 数据



traceparent 是此次接口调用的一段唯一值,由 traceId 和 spanId 等信息组成。
const traceParent = `${VERSION}-${spanContext.traceId}-${spanContext.spanId}-0${Number(spanContext.traceFlags || TraceFlags.NONE).toString(16)}`
接口请求头中加入非标准请求头,会导致接口跨域(可以参见文档 Skywalking 前后端链路打通)。
因此我们还需要在 Nginx 中配置上报数据转发逻辑和接口跨域逻辑,具体来说,需要完成以下两个操作:
1. 配置原有接口跨域逻辑,添加 `Access-Control-Allow-Headers`
2. 配置 trace 上报转发到 APM 逻辑
location /api {
add_header Access-Control-Allow-Headers 'b3, traceparent';
if ($request_method = 'OPTIONS') {
return 204;
}
proxy_pass http://gateway-service:8080;
}

location /v1/traces {
add_header Access-Control-Allow-Origin *;
add_header Access-Control-Allow-Methods 'GET, POST, OPTIONS';
add_header Access-Control-Allow-Headers 'b3, traceparent';
proxy_set_header authentication jqyteBLxxxxxxxxxxBU;
if ($request_method = 'OPTIONS') {
return 204;
}
proxy_pass http://pl.ap-guangzhou.apm.tencentcs.com:55681/v1/traces;
}
完成以上工作后,上报服务和接口服务就都已正常。

三. 前后端链路打通

对于大多数使用 RUM 的前端开发者来说,可能更习惯前端使用 Aegis SDK 作为监控项,对于这类用户,如果开发者同步接入 aegis-web-sdk,可以实现简单高效的前后端链路打通的操作。接入方式可以参见 RUM 的接入文档
因为 Aegis SDK 可以识别接口请求头中的 trace 字段,并且自动采集和上报,所以同步接入后可以看到每个接口的 traceID,用户可以在 RUM 页面单击 trace 字段,绑定 APM 实例后就可以实现自动 trace 跳转。



单击 trace 字段后跳转到 APM 页面。



单击具体的 span 可以看详细的参数信息,可查看 HTTP 接口的参数和自定义上报参数。




四. 简化接入方案

上述的接入方式,对于大多数开发者来说还是稍显麻烦,尤其前端接入 OpenTelemetry 和前后端打通部分,需要引入很多 js 包,并且有很多边界工作需要做。对此,我们希望提供一个更简单高效,并且可以兼容更多全链路监控的方案,不仅限于 OpenTelemetry,也希望可以兼容 Skywalking 等具体实现。

1. 关于前端采集 trace 的思考

要想简化接入方案,我们就需要了解为什么当前的方案复杂,以及如何有效替代当前方案。基本可以总结出难点主要有以下3点:
1. 前端开发者需要了解后端使用某种 trace 协议,并且同步引入对应的 SDK,Skywalking 的 SDK 使用还算简单,但是 OpenTelemetry 的 SDK 使用就稍显复杂。
2. 接口跨域解决起来非常麻烦,涉及到一些边界工作,如果有一些第三方的 API,解决跨域还需要找第三方授权。
3. 前端上报 trace 需要走一层转发,Skywalking 的方案需要把 token 注入到 header 中,OpenTelemetry 的方案需要把 token 塞到 tag 里面,实现起来非常麻烦,且没有比较详细的文档说明。

2. trace 是如何打通的

前文提到接入 OpenTelemetry 后所有打桩的接口都会带上一个请求头,traceparent 或者 b3,这个请求头就是 trace 打通的核心,它主要由 traceId 和 spanId 组成,traceId 会跟随这个接口一直向下,所有的内部服务和请求都带有该字段,每层服务生成对应的 span,最后根据 traceId 把所有 span 连起来形成了 trace 数据链。
如果 Aegis SDK 根据用户协议生成对应的 trace 关键字,并且带入到请求的 header 中,发现也可以实现这个效果:我们在前面的项目中,去掉了所有 OpenTelemetry 的引入,然后 Aegis SDK 中 mock 了一个 traceparent,发现上报正常,trace 也可以正常生成。



不过对比这个 trace 的图跟前面 trace 的图,发现这里 trace 起点是 tomcat,不是前端的 HTTP 请求,所以可以判断这里是缺少了 前端的 span。
因为没有在前端接入 OpenTelemetry 的 sdk,所以这里也不难理解为什么 trace 中少了前端的 span 了。
那么如果用户可以接受忽略 trace 缺少前端 span 这种情况,其实当前的方案已经可以满足 90% 的问题查询 case 了,缺点可能就是如果请求链路在网络层或者 CLB 层断掉的话,就没办法发现断掉的原因。除此之外,前端的数据可以在 RUM 上看,后端的链路可以在 APM 上看,已经是一个比较完美的方案了。
于是我们对当前 Aegis SDK 进行优化,可以适配不同全链路协议的请求头。
目前适配的协议有:traceparentb3sw8sentry-trace,根据下游应用选择对应的协议。

3. 全新接入方案

通过 injectTraceHeader 参数,Aegis SDK 会给所有监控的接口注入对应的请求头,并且自动生成相关协议字段。
对比之前需要几十行的接入代码,并且需要非常复杂的 Nginx 转发配置,当前方案极大的减少了开发者的使用负担。从性能的角度来看,当前方案上报的数据量也远远少于前面的方案,开发者也不需要详细理解 trace 协议的原理,只需要配置即可,可以说是比较方便地实现了“无侵入接入”。
import Aegis from 'aegis-web-sdk'; // 或者通过 cdn 的方式引入

new Aegis({
id: 'xxxxxx',
api: {
injectTraceHeader: 'sw8', // 注意这里目前支持 traceparent,b3,sw8,sentry-trace
injectTraceIgnoreUrls: ['/v1/traces', /rumt-zh.com/], // 忽略不参与注入的接口
},
});

五. 总结

1. 介绍一个后端服务通过 OpenTelemetry SDK 接入到 APM 的具体实践。
2. 介绍前端页面通过 OpenTelemetry SDK 接入到 APM 的具体实践。
3. 介绍前后端监控如何打通,实现全链路监控的方案。
4. 介绍前端使用 Aegis 无侵入接入全链路监控的简化方案,并且适配多种 tracing 协议。
欢迎您进行反馈和交流。