链路追踪
可观测性三剑客
- Metrics
相关服务在运行时被捕获的指标。一个监控指标事件包括指标本身、时间和关联的元数据。比如CPU使用率等。 - Traces
提供一个请求发往应用时所发生事情的全局视野。 - Logs
服务写入的结构或非结构化的文本记录,包括时间戳、元数据。在 OpenTelemetry 中,不属于分布式跟踪或指标的任何数据都是日志,通常缺少上下文。
链路追踪的技术演变
技术架构在近几年的不断发展下,逐渐趋向微服务、服务网格,甚至微函数的形态演变,这种功能拆分和单体缩小的架构模式带来维护成本降低,软件复用率提高等收益的同时,也导致了调用链路复杂,问题定位困难等问题,亟需新的技术方案来提高整体架构的可观测性。
最初,谷歌的一篇论文 «Dapper, a Large-Scale Distributed Systemds Tracing Infrastructure» 介绍了内部未开源的Dapper这个分布式链路追踪系统的设计理念和使用场景。随后,谷歌放出了开源版的OpenCensus。
Opentracing的诞生则试图为分布式追踪创建更多的标准化API及工具,逐渐被Zipkin、Jaeger、skywalking等支持,一时之间成为业界的标杆。
天下大势,分久必合。最终,CNCF孵化的OpenTelemetry合并了Opentracing和OpenCensus,实现了Metric、Tracing、Logging的统一及融合,成为业界的终极和唯一标准。
关键术语
- Span
表示工作/操作的基础单元,跟踪一个请求进行的特定操作,来描述该操作执行期间发生的情况。包括名称、时间相关的数据、结构化的日志、有关跟踪的操作的其他元数据(属性)。 - Collector
用于接收、处理和导出遥测数据的代理 - Exporters
为了可视化和分析遥测数据,需要将数据导出到收集器或后端,例如 Jaeger、Zipkin、Prometheus
选型
初步筛选了几个比较成熟的案例,比如Jaeger,Zipkin,Skywalking,Pinpoint。其中Pinpoint对性能影响最大,且支持的语言最少,基于业务场景的需求考虑最先排除掉。下面针对几个维度对几款产品进行比对
开源项目 | 开发团队 | github仓库 | star数量 | 前端UI支持 | 原生告警支持 | prometheus监控 | 代码侵入 | 链路监控颗粒度 | 后端存储支持 | 采集端语言支持 | 特点 | 云原生部署 |
---|---|---|---|---|---|---|---|---|---|---|---|---|
Jaeger | Uber | jaeger | 17.7k | 支持 | 无 | 支持 | 有 | 接口级 | 内存,Cassandra,kafka,ELK,ClickHouse | Java Python NodeJs Go C++ | UDP传输,性能好,但容易丢包 | 支持k8s |
Zipkin | zipkin | 16.2k | 支持 | 无 | 支持 | 有 | 接口级 | Cassandra, kafka, ELK, MySQL | Java Go PHP JavaScript Python lua | SpringCloud继承好,性能损耗大 | 无 | |
SkyWalking | 吴晟(前华为云) | skywalking | 21.9k | 支持 | 支持 | 支持 | 基于ASM字节码增强技术,无代码侵入 | 代码方法级别,可追踪到中间件调用,比如Redis | ELK,MySQL,PostgreSQL | Java Lua Go PHP NodeJS | 支持dubbo,SpringCloud, SpringBoot集成,依赖多,框架稍重,但性能损耗最小 | 支持k8s |
考虑到代码侵入性和性能损耗问题,暂选择skywalking进行试用。
实践
无代码侵入的魔法
对于Skywalking和Pinpoint,我最感兴趣的是无代码侵入的实现。这是一种叫做字节码增强的技术,底层实现为ASM。
对于Java应用来说,ASM的实现有两种方式:Attach和Javaagent。其中Attach的实现逻辑如下:
- Attach JVM通过Attach API 获取目标JVM,底层通过socket通讯。
- Attach JVM指定目标JVM挂载agent.jar包,执行其中的agentmain方法,对目标JVM类的字节码进行修改。
- Attach JVM通过socket向目标JVM发送指令,目标JVM收到后进行响应,以此实现监控。
而Javaagent的方式更为直观,就是启动的时候加入javaagent
参数,直接指定需要挂载的agent:
java -javaagent:/path/agent,jar=key1=value1,key2=value2 -jar myJar.jar
- 目标JVM通过javaagent参数启动, 执行agent的premain方法;
- agent中通过JVM暴露的接口添加一个Transformer,顾名思义它可以Transform字节码;
- 目标JVM在类加载的时候会触发JVM内置的事件,回调Transformer以实现字节码的增强。 Byte Buddy作为一款基于ASM的类库,兼顾高性能、易用、功能强大等特点。
skywalking上手
版本: v9.5.0
存储: PostgreSQL
架构如图,并没有调研时感觉的笨重 其中agent/探针作为采集器,通常采用gRPC协议进行数据通讯,性能比较理想。backend负责收集和存储数据。GraphQL是一个类似于PromQL的查询工具。
集成了几个java应用后,可以看到自动绘制的拓扑结构,观测效果如下。 除了链路监控外,skywalking还支持端点负载延迟和成功率监控等,对于性能分析也是一大利器!
问题汇总
- 试用期间频繁OOM
系默认存储h2的性能问题,线上需要更换方案,本次试用采用PostgreSQL。 - 负载较重的应用性能影响较大
适当降低采样率,有效缓解 - grafana集成
官方文档的grafana dataSource无效,疑似失去了维护。Native UI倒还可以 - jetty托管的java应用传递javaagent参数失效
官方文档提及的运行参数修改针对jetty.sh中JAVA_OPTIONS进行修改,而systemd托管的jetty配置了forking模式,无法传递参数。本次试用做法是在$JETTY_BASE目录下的start.ini中插入可选参数。 https://github.com/eclipse/jetty.project/issues/7754