07 四个黄金信号(Four Golden Signals)
概述#
面对一个完全陌生的服务,该监控什么?Google SRE 提出了"四个黄金信号"(Four Golden Signals)框架,让每个工程师都能在 15 分钟内搭建"够用"的监控基线。本章深入展开四种信号的定义、测量方法,以及与 RED/USE 方法论的配合,建立"看任何一个系统的第一眼该看什么"的肌肉记忆。
四个黄金信号不是凭空选的,是从"用户对服务的感知维度"反推出来的——用户只关心两件事:服务能不能用(错误)、用起来快不快(延迟)。运维还要关心:服务承受多大压力(流量)、还能撑多久(饱和度)。这四个维度构成了完整的"服务体检报告"。
本章聚焦方法论。Prometheus/Grafana/Loki 的部署配置见 DevOps 路线 Ch25-28。
一、四个黄金信号#
1. Latency(延迟)#
定义:处理一个请求所需的时间。区分"成功请求的延迟"和"失败请求的延迟"——失败请求通常返回很快(直接报错),如果和正常请求混在一起算平均,会掩盖真实的性能问题。
关键度量:
- P50 / P95 / P99 / P999 分位数(不用平均值)
- 按接口粒度区分——整体 P99 好看不代表每个接口都好
- 区分读操作和写操作的延迟
- 区分成功请求和失败请求的延迟分布
为什么不用平均延迟:平均值把长尾吞掉。99% 请求 10ms + 1% 请求 10s → 平均 110ms(看起来还好),但 P99 = 10s(那 1% 用户在受苦)。
更隐蔽的问题是"失败请求拉低平均延迟"。假设 5% 请求超时 30s,95% 请求 200ms。平均 = (95×0.2 + 5×30) / 100 = 1.69s。看起来"延迟 1.7s 还能接受"。但如果只算成功请求,平均是 200ms——服务其实很快,只是 5% 用户在受苦。所以 Google SRE Book 强调:错误请求的延迟要和成功请求分开统计。
PromQL 示例:
# P99 延迟(所有请求)
histogram_quantile(0.99,
sum(rate(http_request_duration_seconds_bucket[5m])) by (le, job)
)
# P99 延迟(只算成功请求)
histogram_quantile(0.99,
sum(rate(http_request_duration_seconds_bucket{status!~"5.."}[5m])) by (le, job)
)
# 慢请求占比(延迟 > 500ms 的请求比例)= 1 - 快请求占比
# le="0.5" 桶算出的是"≤500ms 的快请求占比",取 1 - 才是慢请求占比
1 - (
sum(rate(http_request_duration_seconds_bucket{le="0.5"}[5m]))
/
sum(rate(http_request_duration_seconds_count[5m]))
)延迟 SLI 的两种表达:
- 绝对值型:P99 < 500ms(更直观)
- 比例型:99% 的请求 < 500ms(更贴近用户感受)
两者数学上等价,但比例型更适合做 SLO——它直接表达"多少用户满意"。
2. Traffic(流量)#
定义:服务承受的请求量。对 HTTP 服务是 QPS/RPS,对消息队列是消息速率,对数据库是查询数/连接数,对存储是 IOPS。
为什么重要:
- 流量是其他信号的分母——错误率 = 错误数 / 总流量,不看流量光看错误数会误判
- 流量突增本身可能就是问题——DDoS、热点事件、上游重试风暴
- 容量规划的基础数据
- 流量分布异常是故障信号——某个接口流量突然涨 10 倍可能是上游 bug
关键度量:
# 总 QPS
sum(rate(http_requests_total[5m])) by (job)
# 按接口粒度
sum(rate(http_requests_total[5m])) by (job, handler)
# 按状态码分布
sum(rate(http_requests_total[5m])) by (status)流量分析的常见场景:
- 流量突增:是营销活动、是上游重试风暴、还是 DDoS?
- 流量下降:是上游故障、DNS 问题、还是节日效应?
- 流量分布异常:某个接口突然占 80% 流量,可能是前端 bug 触发循环调用
- 流量模式变化:周末和工作日的流量曲线应该不同,如果一样可能 cron job 配错了
3. Errors(错误)#
定义:请求失败的速率。分为两类:
- 显式错误:HTTP 5xx、超时、连接拒绝
- 隐式错误:HTTP 200 但返回的业务结果是错误的(如"支付成功"实际未扣款——这种更难发现)
关键度量:
# 显式错误率(5xx)
sum(rate(http_requests_total{status=~"5.."}[5m]))
/
sum(rate(http_requests_total[5m]))
# 含 429(限流)的错误率
sum(rate(http_requests_total{status=~"5..|429"}[5m]))
/
sum(rate(http_requests_total[5m]))
# 业务错误率(需要应用层埋点)
sum(rate(business_errors_total[5m]))
/
sum(rate(business_operations_total[5m]))错误分类的细节:
| HTTP 状态码 | 是否算错误 | 说明 |
|---|---|---|
| 2xx | 否 | 成功 |
| 3xx | 否 | 重定向,正常 |
| 4xx(除 429) | 否 | 客户端错误,不算服务失败 |
| 429 | 是 | 限流是服务端容量问题 |
| 499(客户端断开) | 是 | 多因服务端太慢导致客户端放弃,计失败 |
| 5xx | 是 | 服务端错误 |
| 超时 | 是 | 服务无响应 |
| 连接拒绝 | 是 | 服务不可用 |
隐式错误需要通过合成监控(probe)或业务层指标来捕获,纯 HTTP 状态码检测不到。这是 SLO 监控最容易遗漏的一类——一个返回 200 但内容为空的接口,对用户来说就是坏的。
错误率告警的陷阱:低流量服务错误率会剧烈波动。10 个请求里 1 个失败 = 10% 错误率,但这可能只是正常概率事件。需要结合流量绝对值判断——流量 < 10/min 的接口错误率告警意义不大。
4. Saturation(饱和度)#
定义:服务"有多满"——资源使用占总能力的比例。是最容易被忽视但最关键的信号。
需要度量的不是"用了多少",而是"还有多少余量":
| 资源类型 | 饱和度指标 | 说明 |
|---|---|---|
| CPU | CPU throttle 比例 | 不是使用率——throttle 意味着被限制 |
| 内存 | OOM kill 次数、swap 使用 | 不是使用率——OOM 才是真正的饱和 |
| 磁盘 I/O | iowait、队列深度 | 不是 IOPS——队列深度才反映"排队" |
| 网络 | 丢包率、TCP 重传率 | 不是带宽——丢包才反映"拥堵" |
| 应用层 | 线程池队列长度、连接池等待数 | 队列长度 > 0 就是饱和 |
| K8s | Pod 因资源不足 Pending 次数 | Pending 就是饱和 |
| 数据库 | 连接池使用率、慢查询数 | 慢查询堆积就是饱和 |
为什么饱和度重要:饱和度是"滞后指标"——CPU 使用率 90% 时服务可能还 OK,但饱和度 80% 意味着很快就没有余量应对流量波动。在达到使用率上限之前,饱和度就发出预警。
使用率 vs 饱和度的区别:
- 使用率:资源被使用的比例(CPU 80%)
- 饱和度:资源无法立即服务请求的程度(队列长度、等待时间)
CPU 使用率 80% 但没有 throttle、没有队列 = 不饱和。CPU 使用率 60% 但 throttle 严重 = 饱和。饱和度才是性能问题的直接原因。
PromQL 示例:
# CPU throttle 比例
sum(rate(container_cpu_cfs_throttled_seconds_total[5m]))
/
sum(rate(container_cpu_cfs_periods_total[5m]))
# 内存使用率(接近 limit)
sum(container_memory_working_set_bytes)
/
sum(kube_pod_container_resource_limits{resource="memory"})
# Pod Pending
sum(kube_pod_status_phase{phase="Pending"})
# 连接池使用率(需应用埋点)
sum(db_connection_pool_active)
/
sum(db_connection_pool_max)二、RED 方法:微服务监控的最小集#
RED 是 Tom Wilkie(Grafana Labs)提出的微服务监控方法论,可以看作黄金信号在微服务场景下的精炼版:
| 维度 | 含义 | 覆盖的黄金信号 | PromQL |
|---|---|---|---|
| Rate(请求率) | 每秒请求数 | 对应 Traffic | sum(rate(http_requests_total[5m])) by (job) |
| Errors(错误率) | 失败的请求比例 | 对应 Errors | sum(rate(http_requests_total{status=~"5.."}[5m])) / sum(rate(http_requests_total[5m])) |
| Duration(耗时) | 请求的 P99/P95 延迟 | 对应 Latency | histogram_quantile(0.99, sum(rate(http_request_duration_seconds_bucket[5m])) by (le, job)) |
RED 的使用场景:每个微服务都应该有 RED 面板。它是"请求驱动型服务"的监控最小集——如果一个服务要对外提供 API,那 Rate/Errors/Duration 就是它最基本的体检报告。
RED 不覆盖什么:饱和度(Saturation)。所以 RED 方法需要搭配 USE 方法来覆盖资源层面。
RED 面板的标准布局:
Row 1: 服务级总览
- QPS(Rate)
- 错误率(Errors)
- P99 延迟(Duration)
Row 2: 按 endpoint 分解
- 各 endpoint 的 QPS
- 各 endpoint 的错误率
- 各 endpoint 的 P99 延迟
Row 3: 错误详情
- 按状态码分布
- 按错误类型分布三、USE 方法:硬件资源的"体检报告"#
USE 是 Brendan Gregg(Netflix)提出的资源监控方法论,面向 CPU、内存、磁盘、网络等物理/虚拟资源:
| 维度 | 含义 | 问题 |
|---|---|---|
| Utilization(使用率) | 资源用于工作的平均时间占比 | "用得多凶" |
| Saturation(饱和度) | 排队等待资源的工作量 | "排多长的队" |
| Errors(错误) | 资源的错误事件计数 | "有什么坏了" |
Redis 示例:
| 维度 | 指标 |
|---|---|
| Utilization | 内存使用量、CPU 使用率 |
| Saturation | 连接数达到 maxclients 的百分比、慢日志 |
| Errors | 拒绝的连接数、后台 save 失败 |
MySQL 示例:
| 维度 | 指标 |
|---|---|
| Utilization | CPU 使用率、Buffer Pool 命中率 |
| Saturation | 活跃连接数 / max_connections、慢查询数 |
| Errors | Aborted connects、Replication lag |
什么时候用 USE:
- 系统出现性能问题,但 RED 面板看不出异常 → 可能是底层资源瓶颈
- 做容量规划前,先看资源趋势
- 新机器/新节点上架后的基线采集
- 性能压测时的资源监控
USE 方法覆盖的资源类型:
- CPU
- 内存
- 磁盘 IO
- 网络
- GPU(AI 工作负载)
- 应用层资源(线程池、连接池、锁)
四、三个框架配合使用#
┌──────────────┐
│ 黄金信号 │ ← 总框架(延迟/流量/错误/饱和度)
└──────┬───────┘
┌────────────┼────────────┐
▼ ▼ ▼
┌─────────┐ ┌─────────┐ ┌─────────┐
│ RED │ │ USE │ │ 自定义 │
│(服务层)│ │(资源层)│ │(业务层)│
└─────────┘ └─────────┘ └─────────┘排障时的套用顺序:
黄金信号发现异常 → RED 定位到具体服务接口 → USE 排查资源瓶颈- 看黄金信号 Dashboard:延迟变高了,还是错误率升了?
- 如果延迟高 → 用 RED 面板定位是哪个接口的 P99 在涨
- 如果错误率升 → 看错误分布(5xx 类型、是否来自特定上游)
- 排除业务逻辑问题后 → 用 USE 看资源层:CPU throttle?内存压力?磁盘 IO 堵塞?
- 用链路追踪定位到具体 span → 拉对应日志看根因
实战案例:一次延迟告警的排查
1. 黄金信号面板:P99 延迟从 200ms 升到 1.5s
2. RED 面板:发现是 /api/orders 接口的 P99 在涨
3. USE 面板:CPU 使用率正常,但 CPU throttle 比例 30%
4. 链路追踪:/api/orders 的 trace 显示 DB 查询耗时从 50ms 涨到 800ms
5. 日志:DB 慢查询日志显示一条新 SQL 没走索引
6. 根因:某次发布引入了 N+1 查询整个排查 5 分钟,因为每一层都有对应的信号和下钻路径。
五、白盒监控 vs 黑盒监控#
白盒监控(White-box Monitoring)#
基于系统内部暴露的指标:应用吐出的 Prometheus metrics、日志里的结构化字段、链路追踪的 span。
优点:细粒度、能定位根因、有历史趋势
缺点:只有你预先想到去监控的才能看到
黑盒监控(Black-box Monitoring)#
从外部模拟用户行为来探测:HTTP probe、TCP 端口检查、合成事务。
优点:检测到的是用户实际感受到的、能发现"未知的未知"
缺点:只知道"有问题",不知道"为什么有问题"
SRE 的互补策略#
两者必须同时存在:
白盒:Prometheus metrics → SLO 告警、Error Budget、性能分析
黑盒:Blackbox exporter / cuj-prober 合成监控 → 存活检测、兜底告警实战案例(A 旗舰可观测性体系):
- 真实流量监控(Istio istio_requests_total)覆盖有用户流量时的场景
- 合成监控(cuj-prober 5 链路主动拨测)覆盖 7×24 兜底——半夜无流量时验证链路可用
- 两者互补:合成做兜底基线,真实流量做 90% 真用户报错的来源
- 前端 RUM(Real User Monitoring)补足"真实用户在浏览器里感受到什么"
黑盒监控的几个层次:
| 层次 | 工具 | 检测什么 |
|---|---|---|
| 网络层 | ping、tcp ping | 主机可达 |
| 服务层 | blackbox-exporter HTTP probe | 接口返回 200 |
| 业务层 | 合成监控(cuj-prober) | 完整用户旅程成功 |
| 前端层 | RUM(Grafana Faro) | 浏览器端真实体验 |
每一层覆盖不同的"未知未知"——网络层不知道接口是否正常,接口层不知道业务是否正确,业务层不知道前端是否有 JS 报错。
六、Dashboard 设计原则(从培养教材 M5 提取)#
分层结构#
L1 概览盘(Overview) → 整个系统的健康,值班第一眼看
L2 服务盘(Service) → 单服务详情(RED 面板)
L3 实例盘(Instance) → 单 Pod/主机排障从上往下下钻,不需要一屏看 50 个图。
L1 概览盘的内容(值班第一眼):
- 全局错误率(按服务排序)
- 全局 P99 延迟(按服务排序)
- 关键 SLO 状态(绿/黄/红)
- 当前活跃告警数
- 流量趋势(最近 1 小时)
L2 服务盘的内容(深入单个服务):
- RED 三件套
- 按 endpoint 分解
- 错误分布(状态码、错误类型)
- 饱和度指标(CPU throttle、连接池)
L3 实例盘的内容(单机排障):
- 单 Pod/节点的资源使用
- 单 Pod 的日志流
- 单 Pod 的链路追踪
每个图要能回答一个具体问题#
- ❌ "因为有 CPU 指标所以画一张 CPU 图"
- ✅ "这张图回答:CPU throttle 的 Pod 是哪些"
设计原则:
- 每个图有明确的问题:不能因为"有这个指标"就画图
- 优先用阈值着色:绿/黄/红让状态一目了然
- 避免过度堆图:一屏 50 个图 = 没有重点
- 关键指标放最显眼位置:左上角是 SLO 状态
从可观测性培养教材 M1-M6 建立的心智模型#
培养教材建立了一套完整的能力链路:
M1 遥测信号(原材料)
→ M2 采集管道(怎么采)
→ M3 存储查询(存哪、怎么查)
→ M4 告警与 SRE(什么该告)
→ M5 可视化方法论(怎么看) ← 本章聚焦此层
→ M6 架构治理(怎么管)本章的方法论(黄金信号/RED/USE)属于 M5 层——在 M1-M4 基础设施就绪后,回答"怎么读信号"。
排障下钻闭环(检验可观测性架构的标准)#
告警(症状/指标)
→ 概览盘定位异常服务
→ RED 看是慢还是错
→ exemplar/trace_id 跳到慢请求的链路
→ 定位到具体 span
→ 拉那个 span 的日志看根因
→(需要时)profile 看到代码行能不能走通这个闭环,是检验整套可观测性架构"接缝"设计得好不好的终极标准。每一跳都需要跨信号关联键——trace_id 必须在指标、日志、链路里都存在。
七、不同场景下的信号选取#
HTTP API#
- 必看:RED(Rate、Errors、Duration)
- 补充:饱和度(连接池、CPU throttle)
- 黑盒:HTTP probe + 合成监控
消息队列消费者#
- 必看:消费速率、堆积数、消费延迟
- 补充:错误率(消费失败)、重启次数
- 饱和度:消费者数量、单消费者处理时长
数据库#
- 必看:QPS、慢查询数、连接数
- 补充:复制延迟、锁等待
- 饱和度:Buffer Pool 命中率、磁盘 IO
后台批处理任务#
- 必看:任务成功率、执行时长
- 补充:任务排队数、失败重试次数
- 饱和度:worker 数量、队列深度
AI/ML 服务#
- 必看:推理 QPS、推理延迟、推理错误率
- 补充:GPU 利用率、模型版本
- 饱和度:GPU 显存、请求队列长度
不同场景的"四个黄金信号"具体指标不同,但框架不变。理解框架比记住具体指标更重要——遇到新类型的服务,能推导出该看什么。
实战要点#
- 每个新服务必须至少配 RED:Rate、Errors、Duration 三个面板是最小监控集。没有 RED 面板的服务不算"已接入监控"。
- 饱和度不要只看 CPU 使用率。CPU throttle 比例比使用率更有意义——throttle 意味着被限制了。
- 黑盒兜底不可省略:死机开关(Watchdog)是最简单的黑盒——告警系统自己挂了要知道。
- Dashboard 少于 10 个图开始。一屏 50 个图 = 没有图在看——重点图放前面,其余收进"更多"折叠区。
- Prometheus 部署与 Dashboard 连接:Prometheus/Grafana 部署配置见 DevOps Ch25,Recording Rules 优化见 Ch28。
- 延迟要分开统计成功/失败请求。失败请求的延迟混入会扭曲整体延迟分布。
- 低流量服务的错误率告警要谨慎。10 个请求里 1 个失败 = 10% 错误率,但这可能只是统计噪音。
- 每个面板都要能下钻。看到全局 P99 高,要能点击进入按 endpoint 分解;看到 endpoint 高,要能点击进入单 trace。
- 建立"信号关联键":trace_id 必须在指标(exemplar)、日志、链路里都存在,否则下钻闭环会断。
- 饱和度指标要提前定义阈值。不要等饱和度 100% 才告警——80% 就该预警,留 20% 余量。
小结#
四个黄金信号(延迟/流量/错误/饱和度)是监控方法论的总框架。RED(服务层)+ USE(资源层)是落地的两套具体配方。
记住排障闭环:告警 → 概览盘定位异常服务 → RED 看是慢还是错 → 从 exemplar 跳到慢请求的链路 → 拉那个 span 的日志看根因 → 必要时看火焰图到代码行。这个闭环能走通,可观测性架构就合格了;走不通,就说明跨信号关联键没设计好,需要补强。
下一章讲告警工程——有了这些信号,哪些该响告警、怎么响、响给谁。