APM
APM(Application Performance Monitoring,应用性能监控)是面向企业关键业务的实时监控、性能管理与故障定位方案。APM 系统帮助理解系统行为、定位性能问题,以便发生故障时快速恢复服务,降低 MTTR(平均修复时间)。
在单体时代,一次请求的故障通过日志栈就能定位;进入微服务后,同一请求会横跨多个服务、多个集群、多段网络,单看某一个服务的日志无法还原全貌。APM 通过标准化的信号(Signal)把"一个用户请求的经历"串联起来。
为什么需要(微服务带来的问题)
| 单体系统 | 微服务系统 |
|---|---|
| 函数调用栈在同一个进程内 | 调用跨进程、跨主机、跨语言 |
| 日志顺序基本可信 | 各服务日志独立、时间戳可能漂移 |
| 定位 = 读一个应用的日志 | 需要串联多个服务的上下文 |
| 性能瓶颈在本地可复现 | 瓶颈可能在网络、中间件或对端服务 |
所以 APM 要解决的核心问题不是"采集日志",而是上下文传播(Context Propagation):把一个请求的标识(Trace ID / Span ID)从一个服务传到下一个服务,再据此把分散在各处的数据拼回一条完整链路。
Dapper:分布式追踪的奠基论文
目前主流 APM 产品(SkyWalking、Jaeger、Zipkin、Pinpoint 等)都借鉴 Google 的 Dapper 论文。Dapper 提出两个基础概念:
- Trace(链路):一次外部请求引发的、横跨多个服务/进程的全部工作。
- Span(跨度):Trace 中的一段操作,例如一次 RPC 调用、一次 SQL 查询、一段业务逻辑。每个 Span 记录名字、起止时间、调用方(parentSpan)、被调用方(spanId),Span 通过 ID 树状嵌套构成一个 Trace。
Trace = 根 Span + 若干子 Span(树状结构)
Span 关键字段:traceId, spanId, parentSpanId, operationName, startTime, duration核心设计思想:
- 低侵入:用字节码插桩或 RPC 框架拦截实现,尽量不改业务代码。
- 全链路标识:通过
traceId在进程间传递,唯一标识一次调用。 - 采样(Sampling):全量记录成本高,通常按策略采样,用一部分请求代表整体。
- 分析聚合:把 Spans 按 traceId 聚合、合并,重建调用链和耗时分布。
Dapper 风格 vs 统计型指标
| 维度 | Dapper 风格链路(Trace) | 指标(Metric) |
|---|---|---|
| 粒度 | 一次请求一个样本 | 一段时间的聚合值 |
| 信息量 | 能看单次调用完整路径 | 只能看趋势和分布 |
| 成本 | 高(需存储明细) | 低(预聚合) |
| 定位 | 适合钻取定位单次问题 | 适合大盘监控 |
二者互为补充:先用指标发现"哪个服务 RT 异常",再用 Trace 钻取"具体是哪次请求、哪个 Span 慢"。
三大可观测信号
APM 通常把日志、指标、链路合称为可观测性三大支柱(Logs / Metrics / Traces)。
| 信号 | 回答的问题 | 代表工具/标准 |
|---|---|---|
| 日志(Logs) | 某时刻发生了什么具体事件 | ELK、Loki |
| 指标(Metrics) | 一段时间内整体是健康还是异常 | Prometheus |
| 链路(Traces) | 一次请求跨服务经历了什么、慢在哪 | SkyWalking、Jaeger |
更广义的 OpenTelemetry 还会再加入 Profile(性能剖析)等信号,但核心仍是 L/M/T 三者。
三者的关系:Trace 是骨,Metric 是肉,Log 是细节。理想做法是都带有同一个 traceId,这样从 Metric 异常 → Trace 钻取 → Log 看日志可以一键贯通。
上下文传播(Context Propagation)
要让分散的 Span 拼回一条链路,必须把追踪上下文随着调用一起传递。常见两种维度:
- 进程内传播:同一 JVM/进程内的调用,通过线程本地变量(ThreadLocal,SkyWalking 中叫 ContextManager)传递,不落网络报文。
- 跨进程传播:发起下游调用时,把
traceId / spanId / sampler信息塞进协议头,例如 HTTP Header、gRPC Metadata、MQ 消息头。
服务 A 服务 B
│ 生成 traceId │
│ 写 Trace / Span 到 header │
├────────────────────────►│ 读取 header,续接 Span
│ │ 处理,再向下游传递常见的传播协议(Header 名以具体实现为准):
- W3C Trace Context(
traceparent/tracestate):跨语言、跨厂商的事实标准。 - OpenTracing 定义的注入/提取(Inject / Extract)语义。
- SkyWalking 自有协议:通过 HTTP Header
sw8、gRPC Metadata、Kafka Header 传递其二进制/文本格式上下文。
上下文传播是 APM 的正确性基础:如果 Header 名不对齐、或异步线程没传递上下文,链路就会断链、出现"孤儿 Span"。
采样(Sampling)
全量埋点会带来不小开销,且绝大多数请求不值得保存。采样策略决定"记录哪些请求":
| 策略 | 说明 |
|---|---|
| 头部采样(Head Sampling) | 在 Trace 根部决定是否采样,之后整条链路一致 |
| 尾部采样(Tail Sampling) | Span 全部收集后按规则决定哪些入库,更灵活但成本高 |
| 固定采样率 | 如 10%、1%,简单但易漏掉慢请求 |
| 基于规则的采样 | 按 URL、服务、错误状态等决定 |
- 头部采样保证了一条链路要么全采要么全不采,避免"半条链路"。
- 采样后要能看到"大约 X% 的请求被记录",否则统计会失真。
- 错误请求通常需要强制全采,因为问题定位依赖它。
主流 APM 对比
| 工具 | 侧重 | 采样/传播 | 语言 | 存储 |
|---|---|---|---|---|
| SkyWalking | 全栈 APM + 拓扑 | 自有 sw8 / W3C | Java、Go、Node、Py 等多语言 | ES、MySQL、H2 等 |
| Jaeger | 链路追踪为主 | 多协议 | 多语言(OpenTelemetry) | Cassandra、ES 等 |
| Zipkin | 经典链路追踪 | B3 / W3C | 多语言 | ES、内存等 |
| Pinpoint | Java 全栈自动追踪 | 自有协议 | 以 Java 为主 | HBase |
SkyWalking 的特色是把 Trace、Metric、Topology、告警整合到一个平台,且对 Java 生态的字节码插桩很成熟。
生态与标准演进
| 阶段/标准 | 说明 |
|---|---|
| Dapper(2010) | 论文提出 Trace/Span 模型 |
| OpenTracing(2016) | 定义 Trace/Span 与 Inject/Extract API 规范 |
| OpenCensus(2018) | Google 的采集库 |
| OpenTelemetry(2019 起) | OpenTracing + OpenCensus 合并,成为云原生观测统一标准,主导规范 |
| W3C Trace Context | 跨厂商的 HTTP 传播协议标准 |
新一代项目会优先兼容 OpenTelemetry。SkyWalking 既保留自有协议,也支持通过 OpenTelemetry Receiver 接入 OTel 数据。
小结
APM 的核心不是"多一个监控页面",而是用标准化的 Trace / Span 模型 + 跨进程上下文传播,把微服务下散落的请求还原成完整链路,进而支撑指标聚合、拓扑生成和故障定位。理解 Dapper 模型与上下文传播,是读懂任何 APM 平台(包括 SkyWalking)的前提。
基于现有
2.base.md的核心功能 / 关键概念 / 端口已拆分到后面的架构篇;本页重点讲基础概念。