可观测性的三大支柱
前面讲监控那篇,我们搭了 Prometheus 指标和 ELK 日志的骨架。但一个微服务请求要经过网关、订单、库存、支付好几跳,用户说「我下单慢了」,你到底该去看哪个服务的指标、查哪台机器的日志?这就是「可观测性」要回答的问题——它不只是装几个监控面板,而是让你能对一个你事先没预料到的问题,也能查清楚。
业界把可观测性拆成三大支柱,三者各司其职、互为补充:
指标告诉你「出事了」,日志告诉你「当时发生了什么」,链路追踪告诉你「是在哪一跳慢的」。缺任何一个,排障都是盲人摸象。
- Metrics 指标数值化的时间线:QPS、延迟分位数、错误率,最省存储、最适合看趋势和告警。
- Logging 日志离散的文本事件:出错那次请求的上下文,用来还原现场。
- Trace 链路一次请求跨服务的完整调用树,用来定位慢在哪一跳、错误在哪一跳。
Metrics:用指标看趋势
指标是最轻量的观测手段——它只是一个个带时间戳的数字,存得起、算得快、能画趋势。指标分四种类型,选对类型是写好监控的前提:
| 类型 | 记什么 | 典型例子 |
|---|---|---|
| Counter 计数器 | 只增不减的累计值 | 请求总数、错误总数 |
| Gauge 仪表盘 | 可上可下的瞬时值 | 当前内存占用、在线人数 |
| Histogram 直方图 | 把观测值分桶统计分布 | 请求延迟(算 P95、P99) |
| Summary 摘要 | 客户端侧直接算好分位数 | 少数高精度延迟统计 |
最容易犯的错有两个:一是只看平均值。平均延迟 200ms 很好看,但 P99 可能已经飙到 3 秒——大多数用户走得很快,少数用户卡死,平均值会把这种尾部问题完全藏起来。二是把 Gauge 当 Counter 用,拿瞬时值直接告警。告警永远要落在比率和分位数上:错误率 = 错误数 ÷ 总请求数,延迟看 P95/P99,而不是「现在 QPS 比昨天低了」。
Logging:结构化日志定位现场
指标告诉你「错误率涨了」,但用户具体遇到了什么、那次请求传了什么参数,得翻日志。日志能不能用,关键在结构化:不要打一行自由文本,而是每条日志都带上固定的字段。
// 反例:一行自由文本,没法按字段过滤
log.Info("订单123支付失败了,用户456")
// 正例:结构化字段,谁都能检索
log.Info("pay failed",
"order_id", "123",
"user_id", "456",
"amount", 199.00,
"trace_id", "abc-xyz",
"err", "timeout")
这里最关键的字段是 trace_id——一次请求串起来的唯一标识,后面讲 Trace 时它就是那根线。把它塞进每一条日志,你在日志系统里搜一个 trace_id,就能把这次请求跨所有服务打的日志按时间顺序全捞出来。日志的另一条纪律是级别要克制:Info 记正常关键路径,Warn 记可疑但已恢复,Error 记真正需要关注的失败。全是 Error 等于没有 Error,告警很快就会被淹没。
Trace:把一次请求串起来
当一个请求经过网关、订单、库存、支付四个服务,慢了 3 秒。指标面板上每个服务的延迟都正常——那慢在哪一跳?日志里你也不知道该查谁。链路追踪(Tracing)就是给这次请求画一棵树:从进入网关开始,每跨一次服务、每查一次数据库,都记一个 Span(一段耗时),并打上同一个 trace_id 串起来。
Trace abc-xyz 总耗时 3.2s
├─ Span 网关转发 0.02s
├─ Span 订单服务 0.10s
│ └─ Span 订单库查询 0.08s
├─ Span 库存服务 2.90s ← 瓶颈在这
│ └─ Span 库存库查询 2.85s
└─ Span 支付服务 0.15s
一眼就能看出是库存服务的数据库查询吃掉了 2.85 秒。落地工具上,OpenTelemetry 已经成为事实标准的埋点规范——它把指标、日志、 trace 三种数据用同一套 SDK 打出来,再统一送到后端(Jaeger、Tempo、或云厂商的链路服务)。接入顺序建议:先接 OpenTelemetry SDK 把 trace_id 在服务间传起来,再让日志带上它,最后补指标。三者用同一个 trace_id 打通,才叫「全链路」,否则只是三套互不相干的数据。
告警分级:别把人吵醒
监控做满之后,新的痛苦是告警风暴:一上生产,每天几十上百条告警,真正重要的那条反而被略过去了。解决办法不是「少告警」,而是分级——按「要不要现在叫醒人」分三档:
| 级别 | 什么时候发 | 怎么通知 |
|---|---|---|
| P0 紧急 | 核心功能不可用、用户在大量报错,且无人值守会持续恶化 | 电话 + 短信 + IM,oncall 立即响应 |
| P1 重要 | 有退化但未整体不可用,或指标逼近阈值 | IM 群通知,工作时间处理 |
| P2 提示 | 趋势异常、容量预警,不影响当前用户 | 进工单看板,定期复盘 |
几条让告警不扰民的硬纪律:第一,告警条件必须落在「用户可感知」的指标上,比如「支付成功率低于 99%」,而不是「某台机器 CPU 高」——机器 CPU 高但服务正常,不该叫人。第二,要带自动恢复通知,修好了告诉一声,别让人整夜揪心。第三,加抑制与聚合:同一个故障会连带触发十条子告警,先聚合到一个根因告警,把其余的压下去。第四,每条 P0 告警都得配好应急预案——没有手册的告警等于让人现场猜,那不如不叫。
SLI 与 SLO:用目标代替感觉
「我们系统挺稳定的」「这次故障挺严重的」——这些都是感觉。要把稳定性管起来,得先把它变成数字:先定 SLI(你实际测得的服务质量指标),再定 SLO(你承诺的目标)。
SLI:过去 30 天,请求成功率 = 成功请求 ÷ 总请求 = 99.95%
SLO:承诺请求成功率不低于 99.9%
误差预算 = 100% - 99.9% = 0.1%
= 30 天里允许约 43 分钟不可用
这个「误差预算」是整套方法最有用的产物:它给发布和稳定找到了共同语言。预算还很充足,说明系统很稳,可以大胆发新功能、多搞几次发布;预算快烧完了,说明最近故障频繁,那就冻结发布、先去修稳定性。反过来,预算一直没花过,说明你的 SLO 定得太松——系统明明能做到更好。有了 SLO,「要不要这个紧急上线」「这个告警值不值得半夜爬起来」都不用再拍脑袋吵,看数字就行。
一套可落地的可观测清单
别想着一步到位把三大支柱全铺满。按下面这个最小顺序,能在两周内把一个普通线上服务的可观测性补及格:
第一周:把指标和日志补齐
- 每个服务埋 QPS / 错误率 / P95 延迟三个核心指标
- 日志全部结构化,每条带 trace_id
- 错误率、P95 各配一条 P1 告警
第二周:把 trace 和告警分级接上
- 接入 OpenTelemetry,trace_id 跨服务透传
- 按三档重排所有告警,砍掉纯机器类噪音告警
- 给最核心的一两个服务定 SLI / SLO 试运行
最后记三个原则,比工具选型更重要:先接 trace_id 把三件事串起来,再谈加什么工具——否则指标、日志、trace 各存一处,排障照样要跳三个系统。告警少而准,比多而全强一百倍:一个团队如果对告警产生了「习惯性忽略」,那监控就白做了。所有观测都要围着用户走——你是在看用户的请求成功没有、快不快,不是在看服务器累不累。工具会换,这三条不会变。