
“服务可用”常常只是一句模糊的期待:出了故障才发现用户受影响,告警又多到没人相信。SLO(服务等级目标)把这种期待变成可度量的承诺;错误预算则告诉团队,在不牺牲可靠性的前提下还能承受多少失败。
先把三个概念分开
| 概念 | 要回答的问题 | 一个接口示例 |
|---|---|---|
| SLI | 用户实际体验怎样 | 成功请求占有效请求的比例 |
| SLO | 希望保持到什么水平 | 最近 30 天成功率不低于 99.9% |
| 错误预算 | 还能容忍多少失败 | 30 天内允许 0.1% 的有效请求失败 |
先写 SLI,再谈目标。一个常用的可用性 SLI 是:
1 | 可用性 = good_events / valid_events |
valid_events 是应该被统计的请求,good_events 是满足体验条件的请求。条件不要只看 HTTP 状态码:下单接口即使返回 200,若异步写入没有成功,也未必算“好”。反过来,用户主动取消或明确的业务校验失败,是否计入分母也要先写清楚。
从用户旅程选择 SLI

不要为每个内部组件都设面向用户的 SLO。先列出登录、搜索、提交订单等用户动作,再为关键路径选少量指标。缓存命中率、队列积压等内部指标更适合作为定位原因的运行指标。
| 场景 | 推荐 SLI | “好事件”定义示例 |
|---|---|---|
| 同步 API | 可用性 + 延迟 | 非服务端错误,且 95% 请求在 300ms 内完成 |
| 异步任务 | 完成及时性 | 在承诺时限内完成且结果正确 |
| 流式消费 | 处理成功率 | 成功落库或被明确送入可追踪的死信队列 |
指标还要防止“分母漂移”。版本发布、采样策略或路由规则变化前后,分母口径必须保持一致;把探活、压测和内部批量流量混进去,会让用户体验被漂亮的数字掩盖。最实用的做法是把计算查询、标签过滤条件和例外分类放进代码仓库,并为它们做评审。
把百分比换算成可消费预算
例如,一个 30 天的时间可用性 SLO 为 99.9%,意味着最多允许约 43 分 12 秒不可用。若一个月有 200 万个有效请求,同样的目标则允许约 2,000 个坏请求。百分比抽象,预算却可以指导动作:当前已经花掉 70%,就应优先修复已知风险,而不是继续叠加高风险变更。
可以先把目标写成便于讨论的声明:
1 | slo: |
目标不必一开始就很高。没有基线时,先观察一段时间的真实表现,再与业务方确认用户可接受的失败与等待。把目标定成 100% 往往只会制造无效告警,并驱动团队为极少见的故障投入不成比例的成本。
用消耗速度设计告警

只在“30 天成功率低于 99.9%”时报警太慢:预算可能早已在一小时内耗尽。更及时的指标是燃尽率(burn rate):
1 | 燃尽率 = 当前坏事件比例 / (1 - SLO 目标) |
燃尽率为 1,表示正以刚好在整个窗口耗尽预算的速度失败。对 99.9% 目标,燃尽率 14.4 连续一小时大约会花掉月度预算的 2%,足以触发需要值班人员处理的快速告警。告警条件应同时查看短窗口和长窗口:短窗口发现突发,长窗口排除几分钟的偶发抖动。
1 | alerts: |
阈值不是照抄就能用。先用历史故障回放:一次真实的依赖超时、一次发布回滚、一次区域网络抖动,分别会怎样触发?值班人员收到告警后能否在十分钟内找到请求量、坏事件、版本和依赖的关联视图?不能回答的问题,要么补仪表盘,要么调整告警。
让预算进入发布决策
错误预算的价值不在于做一张红绿看板,而在于为风险取舍提供规则。预算充足时,可以安排灰度、重构和容量实验;预算接近耗尽时,暂停非必要发布,优先处理反复出现的失败模式。这里的“暂停”应有明确范围和退出条件,避免把一次局部问题升级成无限期冻结。
| 常见误区 | 更稳的做法 |
|---|---|
| 每个服务都设同一个 99.9% | 按用户旅程、成本与历史基线分别设定 |
| 只看月度百分比 | 同时看预算余量与燃尽速度 |
| 4xx 全部忽略 | 明确区分业务拒绝、客户端取消与异常请求 |
| 告警后只看日志 | 从 SLI 看板跳转到版本、依赖和追踪上下文 |
落地清单
- 选出一条高价值用户旅程,先定义一个可解释的 SLI。
- 写清有效事件、好事件和例外请求的口径,并把查询纳入评审。
- 用 30 天窗口和可接受的业务损失确定初始 SLO。
- 将预算余量、燃尽率、请求量和版本信息放在同一排障视图。
- 用短长双窗口设置快速与慢速告警,并用历史事故验证噪音。
- 把预算状态纳入发布评审,定期复盘目标是否仍符合用户体验。
SLO 不会消灭故障,却能让告警、发布和修复都有一致依据。从一条关键路径开始,把口径和行动写清楚,可靠性才会成为可持续改进的工程能力。