服务稳定性之 LMAT 和 USED
Posted on Fri 06 March 2026 in Tech • 7 min read
服务稳定性之 LMAT 和 USED
隐患险于明火,防范胜于救灾,责任重于泰山。
这句话用在消防安全上恰如其分,用在服务稳定性上同样贴切。
线上故障就像火灾——等你看到火光(用户投诉)时,往往已经烧了一阵子了。真正的功夫不在救火,而在防火:你能不能在火苗刚起时就发现它?你有没有一套系统化的巡检机制?你的告警是不是真的能叫醒该叫醒的人?
本文介绍两个我在实践中反复使用的方法论框架:
- LMAT:Log, Metrics, Alert, Trace —— 可观测性的四根支柱
- USED:Usage, Saturation, Error, Delay —— 资源健康度的四个维度
它们一个回答"用什么手段观测",一个回答"观测什么内容",组合起来就是一套完整的服务稳定性保障体系。
┌─────────────────────────────────┐
│ 服务稳定性保障体系 │
├────────────────┬────────────────┤
│ LMAT │ USED │
│ (怎么观测) │ (观测什么) │
├────────────────┼────────────────┤
│ Log 日志 │ Usage 用量 │
│ Metrics 度量 │ Saturation 饱和│
│ Alert 告警 │ Error 错误 │
│ Trace 追踪 │ Delay 延迟 │
└────────────────┴────────────────┘
一、LMAT:可观测性的四根支柱
可观测性(Observability)这个词这几年被说烂了,但很多团队的实践仍然停留在"有日志就行"的阶段。LMAT 框架把可观测性拆成四个层次,每一层解决不同的问题。
1.1 Log(日志)—— 事后取证的第一现场
日志是最古老、最直觉的观测手段。出了问题,第一反应就是"看日志"。
日志的核心价值:事后分析和取证。
[2026-03-06 10:23:45.123] ERROR [order-service] [trace_id=abc123]
Failed to process order #98765: PaymentGatewayTimeout
user_id=u_42 amount=299.00 retry=2/3
at com.example.OrderService.processPayment(OrderService.java:142)
一条好的日志应该包含:
| 要素 | 说明 | 示例 |
|---|---|---|
| 时间戳 | 精确到毫秒 | 2026-03-06T10:23:45.123Z |
| 级别 | ERROR/WARN/INFO/DEBUG | ERROR |
| 服务名 | 哪个服务产生的 | order-service |
| Trace ID | 关联到分布式追踪 | trace_id=abc123 |
| 上下文 | 业务关键字段 | user_id, order_id, amount |
| 错误详情 | 具体什么错 | PaymentGatewayTimeout |
日志的常见问题:
- 日志太多:每秒几万条,存储成本爆炸,搜索慢如蜗牛
- 日志太少:出了问题找不到关键信息,只能加日志重新部署
- 日志没结构:自由文本格式,无法聚合分析
- 日志没关联:缺少 Trace ID,无法串联一次请求的完整链路
最佳实践:
# ❌ 不好的日志
logger.error("something went wrong")
# ✅ 好的日志:结构化 + 上下文
logger.error(
"Payment gateway timeout",
extra={
"trace_id": ctx.trace_id,
"user_id": user.id,
"order_id": order.id,
"amount": order.amount,
"gateway": "stripe",
"retry_count": retry_count,
"latency_ms": elapsed_ms,
}
)
日志分级策略:
生产环境日志级别建议:
INFO ── 关键业务事件(订单创建、支付成功、用户登录)
WARN ── 可恢复的异常(重试成功、降级触发、缓存未命中)
ERROR ── 不可恢复的错误(支付失败、数据库连接断开)
DEBUG ── 仅在排查问题时动态开启(通过配置中心热更新)
1.2 Metrics(度量)—— 实时体检的仪表盘
如果说日志是"病历本",那度量就是"体检报告"。
度量的核心价值:实时感知系统的健康状态。
度量是数值型的时间序列数据,天然适合聚合、对比、告警。
# Prometheus 格式的度量
http_requests_total{service="order", method="POST", status="200"} 12345
http_request_duration_seconds{service="order", quantile="0.99"} 0.85
order_payment_errors_total{gateway="stripe", reason="timeout"} 42
四种度量类型:
| 类型 | 说明 | 典型用途 |
|---|---|---|
| Counter | 只增不减的计数器 | 请求总数、错误总数、Token 消耗 |
| Gauge | 可增可减的瞬时值 | CPU 使用率、队列长度、连接数 |
| Histogram | 值的分布(分桶统计) | 延迟分布、请求大小分布 |
| Summary | 值的分位数(客户端计算) | P50/P99 延迟 |
度量的黄金法则——RED 和 USE:
面向请求的服务(API、Web)用 RED:
Rate ── 每秒请求数
Error ── 错误率
Duration ── 延迟
面向资源的组件(DB、Cache、Queue)用 USE:
Utilization ── 利用率
Saturation ── 饱和度
Errors ── 错误数
度量命名规范(Prometheus 风格):
# 格式:<namespace>_<subsystem>_<name>_<unit>
# 示例:
http_server_requests_total # Counter
http_server_request_duration_seconds # Histogram
db_pool_connections_active # Gauge
mq_consumer_lag_messages # Gauge
1.3 Alert(告警)—— 把被动救火变成主动预警
度量告诉你"现在怎么样",告警告诉你"什么时候该行动"。
告警的核心价值:在用户感知之前发现问题。
# Prometheus 告警规则示例
groups:
- name: order-service-alerts
rules:
# 错误率告警
- alert: HighErrorRate
expr: |
sum(rate(http_requests_total{service="order",status=~"5.."}[5m]))
/ sum(rate(http_requests_total{service="order"}[5m])) > 0.01
for: 3m
labels:
severity: critical
annotations:
summary: "订单服务错误率 > 1%: {{ $value | humanizePercentage }}"
runbook: "https://wiki.example.com/runbook/order-high-error-rate"
# 延迟告警
- alert: HighLatency
expr: |
histogram_quantile(0.99,
rate(http_request_duration_seconds_bucket{service="order"}[5m])
) > 2.0
for: 5m
labels:
severity: warning
annotations:
summary: "订单服务 P99 延迟 > 2s: {{ $value }}s"
告警的三大陷阱:
| 陷阱 | 症状 | 解决方案 |
|---|---|---|
| 告警风暴 | 一个故障触发几十条告警,淹没真正重要的信息 | 告警聚合 + 抑制规则 + 分级 |
| 告警疲劳 | 太多误报,on-call 开始忽略告警 | 定期清理无效告警,每条告警必须有 runbook |
| 告警缺失 | 故障发生了但没有告警 | 定期做故障演练,验证告警覆盖率 |
告警分级:
P0 (Critical) ── 用户受影响,立即响应
例:支付成功率 < 99%、核心 API 不可用
通知:电话 + 短信 + IM
响应:5 分钟内
P1 (High) ── 即将影响用户,尽快处理
例:错误率上升、延迟恶化、资源接近饱和
通知:IM + 邮件
响应:30 分钟内
P2 (Medium) ── 需要关注,工作时间处理
例:非核心服务异常、日志错误增多
通知:IM
响应:4 小时内
P3 (Low) ── 信息性告警,下个迭代处理
例:证书即将过期、磁盘使用率 > 70%
通知:邮件 / 工单
响应:1 周内
好的告警 = 可执行的告警。 每条告警都应该附带 runbook(操作手册),告诉 on-call 工程师:这个告警意味着什么?应该怎么排查?常见的根因有哪些?
1.4 Trace(追踪)—— 分布式系统的 X 光片
在微服务架构下,一个用户请求可能经过 5-10 个服务。出了问题,你怎么知道是哪个环节慢了、哪个环节报错了?
追踪的核心价值:还原一次请求的完整链路。
Trace: abc-123-def
│
├── [order-service] POST /api/orders (120ms)
│ ├── [order-service] validate_request (2ms)
│ ├── [order-service] check_inventory (15ms)
│ │ └── [inventory-service] GET /api/stock (12ms)
│ ├── [order-service] process_payment (95ms) ⚠️ 慢!
│ │ └── [payment-service] POST /api/charge (90ms)
│ │ └── [stripe-gateway] external_call (85ms) 🔥 根因
│ └── [order-service] send_notification (5ms)
│ └── [notification-service] POST /api/notify (3ms)
一眼就能看出:支付环节耗时 95ms,其中 85ms 花在了 Stripe 外部调用上。
追踪的三个核心概念:
| 概念 | 说明 |
|---|---|
| Trace | 一次完整请求的全链路,由唯一的 Trace ID 标识 |
| Span | 链路中的一个操作单元(一次函数调用、一次 RPC、一次 DB 查询) |
| Context Propagation | 在服务间传递 Trace ID,串联所有 Span |
OpenTelemetry 实现:
from opentelemetry import trace
from opentelemetry.instrumentation.fastapi import FastAPIInstrumentor
from opentelemetry.instrumentation.httpx import HTTPXClientInstrumentor
from opentelemetry.instrumentation.sqlalchemy import SQLAlchemyInstrumentor
# 自动注入:FastAPI 入口 + HTTP 客户端 + 数据库
FastAPIInstrumentor.instrument_app(app)
HTTPXClientInstrumentor().instrument()
SQLAlchemyInstrumentor().instrument(engine=engine)
# 手动注入:业务关键路径
tracer = trace.get_tracer("order-service")
async def process_order(order):
with tracer.start_as_current_span("process_order") as span:
span.set_attribute("order.id", order.id)
span.set_attribute("order.amount", order.amount)
with tracer.start_as_current_span("check_inventory"):
await inventory_client.check(order.items)
with tracer.start_as_current_span("process_payment"):
result = await payment_client.charge(order)
span.set_attribute("payment.gateway", result.gateway)
1.5 LMAT 的协同效应
LMAT 四者不是孤立的,它们的真正威力在于关联:
告警触发 (Alert)
│
▼ "订单服务错误率 > 1%"
查看度量 (Metrics)
│
▼ 发现 payment 相关的 5xx 激增,从 10:15 开始
查看追踪 (Trace)
│
▼ 抽样几条失败请求,发现都卡在 stripe-gateway,超时 30s
查看日志 (Log)
│
▼ 搜索 trace_id,找到具体错误:
"Stripe API returned 503: Service Temporarily Unavailable"
│
▼ 根因确认:Stripe 侧故障,触发降级到备用支付通道
关联的关键是 Trace ID——它是串联 Log、Metrics、Trace 的纽带。
二、USED:资源健康度的四个维度
LMAT 回答了"用什么手段观测",USED 回答了"观测什么内容"。
USED 是我在 Brendan Gregg 的 USE Method(Utilization, Saturation, Errors)基础上扩展的版本,增加了 Delay(延迟) 维度,更适合面向用户的服务场景。
2.1 Usage(用量)—— 你用了多少?
用量是最基础的度量维度:你的资源用了多少,还剩多少。
┌─────────────────────────────────────────────┐
│ Usage 度量矩阵 │
├──────────────┬──────────────────────────────┤
│ 资源 │ 度量指标 │
├──────────────┼──────────────────────────────┤
│ CPU │ 使用率 (%)、核数 │
│ 内存 │ 已用/总量、RSS、堆内存 │
│ 磁盘 │ 已用/总量、IOPS │
│ 网络 │ 带宽使用率、连接数 │
│ 数据库连接池 │ 活跃连接/最大连接 │
│ 线程池 │ 活跃线程/最大线程 │
│ 消息队列 │ 队列深度、消费速率 │
│ LLM API │ Token 使用量、API 调用次数 │
│ 存储 │ 对象数量、存储空间 │
└──────────────┴──────────────────────────────┘
用量度量的关键不是"现在用了多少",而是"趋势"和"容量":
# 容量预测:按当前增长速率,多久会用完?
predict_linear(disk_used_bytes[7d], 30 * 24 * 3600) > disk_total_bytes
# 含义:按过去 7 天的趋势,30 天后磁盘会不会满?
2.2 Saturation(饱和度)—— 你还能撑多久?
饱和度比用量更进一步:不只是"用了多少",而是"还能不能接受更多工作"。
用量 vs 饱和度的区别:
| 用量 (Usage) | 饱和度 (Saturation) | |
|---|---|---|
| CPU | 使用率 80% | 运行队列长度 > CPU 核数 |
| 内存 | 已用 12GB/16GB | 开始 swap,OOM kill |
| 连接池 | 80/100 连接在用 | 有请求在等待连接(排队) |
| 消息队列 | 队列中有 1000 条消息 | 消费速率 < 生产速率(积压) |
| 线程池 | 8/10 线程在忙 | 任务队列开始堆积 |
饱和度是"即将出问题"的信号。 用量高不一定有问题(可能就是正常高峰),但饱和了一定有问题。
# 饱和度告警示例
- alert: DBConnectionPoolSaturated
expr: |
db_pool_pending_requests > 0
for: 1m
annotations:
summary: "数据库连接池饱和:有请求在排队等待连接"
- alert: MessageQueueBacklog
expr: |
rate(mq_produced_total[5m]) > rate(mq_consumed_total[5m]) * 1.1
for: 10m
annotations:
summary: "消息队列积压:生产速率持续超过消费速率"
2.3 Error(错误)—— 什么东西坏了?
错误是最直接的健康信号。但不是所有错误都一样重要。
错误分类:
错误严重程度金字塔:
▲ 数据损坏 / 数据丢失
│ ───────────────────── 致命
│ 服务不可用 (503)
│ ───────────────────── 严重
│ 业务逻辑错误 (500)
│ ───────────────────── 高
│ 上游依赖超时 (504)
│ ───────────────────── 中
│ 客户端错误 (4xx)
│ ───────────────────── 低
▼ 可重试的瞬时错误
错误率 vs 错误数:
# 错误数:绝对值,适合低流量场景
http_errors_total{status="500"} > 10 # 过去 5 分钟超过 10 个 500
# 错误率:相对值,适合高流量场景
rate(http_errors_total{status=~"5.."}[5m])
/ rate(http_requests_total[5m]) > 0.01 # 错误率 > 1%
错误预算(Error Budget):
SRE 的核心理念之一——如果你的 SLO 是 99.9%,那你每月有 43 分钟的错误预算:
可用性 SLO 月度错误预算
───────── ──────────
99% 7.3 小时
99.9% 43.2 分钟
99.95% 21.6 分钟
99.99% 4.3 分钟
# 错误预算消耗速率告警
- alert: ErrorBudgetBurnRate
expr: |
(
1 - (
sum(rate(http_requests_total{status!~"5.."}[1h]))
/ sum(rate(http_requests_total[1h]))
)
) * 720 > 1
annotations:
summary: "错误预算消耗过快:按当前速率,本月预算将提前耗尽"
2.4 Delay(延迟)—— 用户等了多久?
延迟是用户体验最直接的度量。一个请求即使成功了,如果等了 10 秒,用户体验也是灾难性的。
延迟度量的关键原则:看分位数,不看平均值。
为什么平均值会骗人?
假设 100 个请求:
99 个耗时 10ms
1 个耗时 10,000ms (10秒)
平均值 = (99×10 + 1×10000) / 100 = 109ms ← 看起来还行?
P99 = 10,000ms ← 真相:1% 的用户等了 10 秒!
延迟分层度量:
用户感知延迟(端到端)
│
├── 网络延迟(DNS + TCP + TLS)
│
├── 网关延迟(负载均衡、WAF)
│
├── 服务处理延迟
│ ├── 业务逻辑
│ ├── 数据库查询
│ ├── 缓存访问
│ ├── 外部 API 调用
│ └── 序列化/反序列化
│
└── 响应传输延迟
# 延迟 Histogram(Prometheus)
http_request_duration_seconds = Histogram(
"http_request_duration_seconds",
"Request duration",
["service", "method", "endpoint"],
buckets=[0.01, 0.025, 0.05, 0.1, 0.25, 0.5, 1.0, 2.5, 5.0, 10.0]
)
# 关键 PromQL
# P50 延迟
histogram_quantile(0.50, rate(http_request_duration_seconds_bucket[5m]))
# P99 延迟
histogram_quantile(0.99, rate(http_request_duration_seconds_bucket[5m]))
# Apdex 分数(满意 < 0.5s,容忍 < 2s)
(
sum(rate(http_request_duration_seconds_bucket{le="0.5"}[5m]))
+ sum(rate(http_request_duration_seconds_bucket{le="2.0"}[5m]))
) / 2 / sum(rate(http_request_duration_seconds_count[5m]))
三、LMAT × USED:构建完整的度量矩阵
把 LMAT 和 USED 交叉组合,就得到一个完整的服务稳定性度量矩阵:
│ Usage │ Saturation │ Error │ Delay
──────────────┼─────────────┼─────────────┼─────────────┼────────────
Log (日志) │ 资源使用 │ 排队/等待 │ 错误堆栈 │ 慢查询日志
│ 变更记录 │ 限流日志 │ 异常详情 │ 超时日志
──────────────┼─────────────┼─────────────┼─────────────┼────────────
Metrics(度量) │ CPU/Mem/ │ 队列深度 │ 错误率 │ P50/P99
│ Disk/连接数 │ 线程等待数 │ 错误数 │ TTFB/TTLB
──────────────┼─────────────┼─────────────┼─────────────┼────────────
Alert (告警) │ 容量预警 │ 饱和告警 │ 错误率告警 │ 延迟告警
│ 趋势预测 │ 积压告警 │ 预算消耗 │ SLO 违规
──────────────┼─────────────┼─────────────┼─────────────┼────────────
Trace (追踪) │ 调用次数 │ 排队耗时 │ 错误链路 │ 慢链路
│ 依赖拓扑 │ 重试次数 │ 异常传播 │ 瓶颈定位
这个矩阵的使用方式:
- 日常巡检:横向看 Metrics 行,快速扫描 USED 四个维度
- 故障排查:纵向看 Error 列,从 Alert 触发 → Metrics 定位 → Trace 追踪 → Log 取证
- 容量规划:横向看 Usage + Saturation,结合趋势预测
- SLO 管理:聚焦 Error + Delay 两列
四、实战:一个服务的 LMAT × USED 落地
以一个订单服务为例,展示如何落地这套体系。
4.1 度量定义
from prometheus_client import Counter, Histogram, Gauge
# === Usage ===
db_pool_active = Gauge(
"db_pool_active_connections",
"Active database connections"
)
cache_memory_bytes = Gauge(
"cache_memory_used_bytes",
"Cache memory usage"
)
# === Saturation ===
db_pool_pending = Gauge(
"db_pool_pending_requests",
"Requests waiting for a DB connection"
)
thread_pool_queue_size = Gauge(
"thread_pool_queue_size",
"Tasks waiting in thread pool queue"
)
# === Error ===
http_requests_total = Counter(
"http_requests_total",
"Total HTTP requests",
["method", "endpoint", "status"]
)
business_errors_total = Counter(
"business_errors_total",
"Business logic errors",
["error_type"] # payment_failed, inventory_insufficient, ...
)
# === Delay ===
http_duration = Histogram(
"http_request_duration_seconds",
"HTTP request duration",
["method", "endpoint"],
buckets=[0.01, 0.05, 0.1, 0.25, 0.5, 1.0, 2.5, 5.0, 10.0]
)
db_query_duration = Histogram(
"db_query_duration_seconds",
"Database query duration",
["query_type"], # select, insert, update
buckets=[0.001, 0.005, 0.01, 0.05, 0.1, 0.5, 1.0]
)
upstream_call_duration = Histogram(
"upstream_call_duration_seconds",
"Upstream service call duration",
["service", "method"],
buckets=[0.01, 0.05, 0.1, 0.5, 1.0, 5.0, 10.0]
)
4.2 告警规则
groups:
- name: order-service-used
rules:
# Usage: 连接池使用率 > 80%
- alert: DBPoolHighUsage
expr: db_pool_active_connections / db_pool_max_connections > 0.8
for: 5m
labels: { severity: warning }
annotations:
summary: "DB 连接池使用率 > 80%"
runbook: "https://wiki/runbook/db-pool-high-usage"
# Saturation: 有请求在排队
- alert: DBPoolSaturated
expr: db_pool_pending_requests > 0
for: 1m
labels: { severity: critical }
annotations:
summary: "DB 连接池饱和,请求排队中"
runbook: "https://wiki/runbook/db-pool-saturated"
# Error: 5xx 错误率 > 0.1%
- alert: HighErrorRate
expr: |
sum(rate(http_requests_total{status=~"5.."}[5m]))
/ sum(rate(http_requests_total[5m])) > 0.001
for: 3m
labels: { severity: critical }
annotations:
summary: "错误率 > 0.1%: {{ $value | humanizePercentage }}"
runbook: "https://wiki/runbook/high-error-rate"
# Delay: P99 > 2s
- alert: HighP99Latency
expr: |
histogram_quantile(0.99,
rate(http_request_duration_seconds_bucket[5m])) > 2.0
for: 5m
labels: { severity: warning }
annotations:
summary: "P99 延迟 > 2s: {{ $value }}s"
runbook: "https://wiki/runbook/high-latency"
4.3 Grafana 面板布局
┌─────────────────────────────────────────────────────────┐
│ 订单服务监控面板 │
├──────────┬──────────┬──────────┬────────────────────────┤
│ QPS │ 成功率 │ P99 延迟 │ 错误预算剩余 │
│ 1,234/s │ 99.95% │ 850ms │ 72% (31min) │
├──────────┴──────────┴──────────┴────────────────────────┤
│ │
│ ┌─── Usage ──────────────┐ ┌─── Saturation ────────┐ │
│ │ CPU / Mem / 连接池使用率 │ │ 队列深度 / 等待线程数 │ │
│ └────────────────────────┘ └───────────────────────┘ │
│ │
│ ┌─── Error ──────────────┐ ┌─── Delay ─────────────┐ │
│ │ 错误率趋势 + 错误类型 │ │ P50/P95/P99 延迟趋势 │ │
│ │ 分布 (饼图) │ │ + 延迟分布热力图 │ │
│ └────────────────────────┘ └───────────────────────┘ │
│ │
│ ┌─── 依赖健康度 ─────────────────────────────────────┐ │
│ │ payment-service: ✅ 99.9% / 120ms │ │
│ │ inventory-service: ✅ 99.95% / 45ms │ │
│ │ notification-service: ⚠️ 99.5% / 350ms │ │
│ └────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────┘
五、防范胜于救灾:从被动到主动
有了 LMAT 和 USED,我们就有了"看"的能力。但光能看还不够,还要能"防"。
5.1 三道防线
第一道防线:预防(防患于未然)
├── 容量规划:基于 Usage 趋势预测,提前扩容
├── 混沌工程:主动注入故障,验证系统韧性
├── 变更管控:灰度发布 + 自动回滚
└── 代码质量:静态分析 + 单元测试 + 集成测试
第二道防线:发现(早发现早处理)
├── 实时告警:基于 USED 四维度的告警规则
├── 异常检测:基于历史基线的自动异常识别
├── 巡检机制:定期自动化健康检查
└── 值班制度:7×24 on-call 轮值
第三道防线:恢复(快速止血)
├── 自动恢复:熔断、降级、限流、重试
├── 快速回滚:一键回滚到上一个稳定版本
├── 预案执行:预定义的故障处理 SOP
└── 事后复盘:每次故障都要写 Post-Mortem
5.2 故障处理的 OODA 循环
Observe (观察)
│ 告警触发,查看 Metrics 面板
▼
Orient (判断)
│ 结合 Trace + Log,定位根因
▼
Decide (决策)
│ 选择处置方案:回滚?扩容?降级?
▼
Act (行动)
│ 执行操作,验证恢复
▼
回到 Observe,确认问题解决
5.3 Post-Mortem 模板
每次故障都是学习的机会。一份好的 Post-Mortem 应该包含:
## 故障报告:[标题]
### 基本信息
- 时间:2026-03-06 10:15 ~ 10:42 (27 分钟)
- 影响:订单服务错误率升至 5%,约 1200 个订单受影响
- 严重程度:P0
### 时间线
- 10:15 Stripe API 开始返回 503
- 10:18 告警触发:HighErrorRate
- 10:20 On-call 工程师确认问题
- 10:25 决定启用备用支付通道
- 10:30 备用通道上线,错误率开始下降
- 10:42 错误率恢复正常
### 根因
Stripe 侧基础设施故障,导致 API 间歇性返回 503。
### 改进措施
| 措施 | 负责人 | 截止日期 |
|------|--------|---------|
| 支付通道自动故障转移 | @alice | 2026-03-20 |
| 增加 Stripe 健康检查探针 | @bob | 2026-03-13 |
| 补充支付降级的告警规则 | @carol | 2026-03-10 |
### 经验教训
- 我们的告警在 3 分钟内触发,响应及时 ✅
- 但备用支付通道的切换是手动的,耗时 10 分钟 ❌
- 需要实现自动故障转移
六、总结
LMAT —— 怎么观测?
Log 日志 事后取证的第一现场
Metrics 度量 实时体检的仪表盘
Alert 告警 把被动救火变成主动预警
Trace 追踪 分布式系统的 X 光片
USED —— 观测什么?
Usage 用量 你用了多少?趋势如何?
Saturation 饱和度 你还能撑多久?
Error 错误 什么东西坏了?多严重?
Delay 延迟 用户等了多久?
LMAT × USED = 完整的服务稳定性度量矩阵
隐患险于明火——线上的隐患(内存泄漏、连接池耗尽、磁盘缓慢增长)比突发故障更危险,因为它们不会触发告警,却会在某个深夜突然爆发。USED 中的 Usage 和 Saturation 就是用来发现这些隐患的。
防范胜于救灾——与其在故障发生后手忙脚乱地排查,不如提前建好 LMAT 体系,让问题在萌芽阶段就被发现。一条好的告警规则,胜过十个 on-call 工程师的通宵排查。
责任重于泰山——服务稳定性不是 SRE 团队的事,是每个工程师的事。写代码时埋好度量点,上线前配好告警规则,出了问题写好 Post-Mortem——这就是对用户负责,对团队负责。
最好的监控系统,不是在故障发生时告诉你"出事了", 而是在故障发生前告诉你"要出事了"。
参考资料:
- Brendan Gregg, "The USE Method" (https://www.brendangregg.com/usemethod.html)
- Google SRE Book, Chapter 6: Monitoring Distributed Systems
- OpenTelemetry Documentation (https://opentelemetry.io/docs/)
- Tom Wilkie, "The RED Method" (https://grafana.com/blog/2018/08/02/the-red-method-how-to-instrument-your-services/)