可观测性与故障处理
可观测性不是“日志很多”,而是能从外部现象推断内部状态:用户为什么失败,请求在哪一层变慢,哪个版本引入了变化,系统还能不能继续承受流量。故障处理则是在信息不完整、时间有限的情况下,先恢复服务,再查明原因。
1. 先定义什么叫正常
跳转到“1. 先定义什么叫正常”没有正常基线,监控只能靠感觉。先从用户视角定义服务指标(SLI):
- 请求成功率。
- 响应延迟的 p50、p95、p99。
- 关键业务流程完成率。
- 数据新鲜度和队列延迟。
- 错误预算与可用性。
再把目标写成 SLO,例如“过去 30 天,99.9% 的登录请求在 1 秒内成功”。SLO 不是装饰承诺,它决定何时需要停止发布、优先修复稳定性,而不是继续加功能。
1.1 错误预算
跳转到“1.1 错误预算”若目标可用性 99.9%,30 天允许约 0.1% 不可用,即大约 43 分钟。错误预算用来做决策:
- 预算充足时,可以推进有风险但回报高的变更。
- 预算快速消耗时,先降低发布频率,修复主要故障。
- 不能因为预算还没用完,就忽略局部用户体验。
指标必须区分“服务器返回 200”和“用户真的成功”。异步任务失败、前端脚本崩溃、第三方依赖不可用,都可能让用户失败,但服务器指标看起来正常。
2. 三类核心信号
跳转到“2. 三类核心信号”2.1 指标
跳转到“2.1 指标”指标是随时间聚合的数值,适合告警和趋势,不适合保存每条详情。
常见类型:
- 计数器:只增不减,如请求总数、错误总数。
- 仪表:当前值,如连接数、队列长度。
- 直方图:按桶统计分布,用于延迟分位数。
- 摘要:预计算分位数,跨实例聚合时要小心。
高基数标签会爆炸。用户 ID、请求 ID、完整 URL 和随机参数不应作为普通指标标签;应使用有限维度和采样/trace 定位单个请求。
2.2 日志
跳转到“2.2 日志”日志是离散事件记录,适合解释“发生了什么”。结构化日志通常比自由文本更容易检索:
{ "time": "2026-10-31T12:00:00Z", "level": "error", "service": "api", "requestId": "req_123", "route": "POST /orders", "code": "DB_TIMEOUT", "durationMs": 5032}日志分级要克制。大量 debug 会拖慢系统并增加存储成本。敏感字段必须脱敏;完整请求体、Cookie、令牌和个人信息不应默认写入。
2.3 Trace
跳转到“2.3 Trace”trace 描述一个请求经过哪些服务、每段耗时多少。一个 trace 由多个 span 组成:
HTTP POST /checkout 320 ms├── auth.validate 5 ms├── inventory.reserve 120 ms│ └── db.query 90 ms├── payment.charge 180 ms│ └── external API 170 ms└── order.persist 12 msspan 应带服务名、操作名、状态、耗时和必要属性。不要把敏感数据塞进 span。跨服务传播 trace context,需要统一标准和采样策略。
3. 采样与关联
跳转到“3. 采样与关联”全量采集可能成本很高。可以:
- 对成功请求低比例采样。
- 对错误、慢请求和高价值业务提高采样率。
- 在入口生成 request ID,贯穿日志、指标 exemplar 和 trace。
- 让客户端错误、服务端日志和后端 trace 能通过同一 ID 关联。
采样意味着不能从“没看到错误”推断没有错误。告警应优先使用聚合指标,定位时再查具体 trace。
4. 仪表盘
跳转到“4. 仪表盘”仪表盘应服务具体问题,而不是把所有图放一页。
4.1 服务总览
跳转到“4.1 服务总览”- 请求量。
- 成功率与各错误码。
- 延迟分位数。
- 饱和度:CPU、内存、连接池、队列、磁盘。
- 依赖健康。
- 发布和配置变更标记。
4.2 业务仪表盘
跳转到“4.2 业务仪表盘”- 下单成功率。
- 支付失败分类。
- 注册漏斗。
- 任务积压与完成时间。
4.3 排障仪表盘
跳转到“4.3 排障仪表盘”按依赖、实例、版本、地区、租户维度查看异常。图上要显示时间范围、单位和数据来源,避免“看起来涨了”但无法解释。
5. 告警
跳转到“5. 告警”好的告警满足:
- 对应真实用户影响或明确的容量风险。
- 有可执行动作。
- 有足够上下文和链接。
- 避免重复和抖动。
- 有负责人和升级路径。
按症状告警,通常比按每个内部指标告警更有效。例如“支付成功率低于阈值”比“某个线程池队列长度超过 10”更接近用户影响。内部指标可以作为原因线索。
5.1 阈值与多窗口
跳转到“5.1 阈值与多窗口”瞬时峰值不一定需要叫醒人。可以用多窗口 burn rate:短时间内错误预算快速消耗时紧急告警,长期缓慢消耗时创建工单。
告警阈值要基于基线和业务容忍度。固定 CPU > 80% 既可能误报,也可能漏掉“CPU 只有 30% 但请求全部排队”的故障。
6. 故障处理流程
跳转到“6. 故障处理流程”发现 → 分诊 → 止血 → 沟通 → 根因分析 → 修复与验证 → 复盘 → 改进项6.1 发现与分诊
跳转到“6.1 发现与分诊”先确认:
- 哪些用户、地区、版本和功能受影响。
- 开始时间和最近变更。
- 是全部失败、部分失败还是变慢。
- 当前影响是否仍在扩大。
不要一开始就追根因。用户正在失败时,优先恢复可用性。
6.2 止血
跳转到“6.2 止血”常见手段:
- 回滚最近发布。
- 关闭功能开关。
- 切流量到健康区域或旧版本。
- 扩容或提高限流额度。
- 降级非核心功能。
- 暂停批处理或后台任务。
止血也要记录时间和操作结果。第二次操作前先确认第一次是否生效。
6.3 沟通
跳转到“6.3 沟通”事故期间需要稳定更新:
- 当前影响。
- 已采取动作。
- 下一次更新时间。
- 用户需要做什么。
不确定原因时不要编造结论。清晰说明“正在验证”比错误归因更有用。
6.4 恢复验证
跳转到“6.4 恢复验证”指标恢复不等于所有数据恢复。检查:
- 错误率和延迟回到基线。
- 队列积压是否消化。
- 失败请求是否可重试或补偿。
- 数据是否一致。
- 客户端缓存和旧版本是否仍受影响。
7. 根因分析
跳转到“7. 根因分析”根因不是一个孤立句子。可以使用“五个为什么”,但要避免把复杂系统故障简化为个人失误。
现象:支付成功率下降为什么:支付服务连接数据库超时为什么:数据库连接池耗尽为什么:慢查询增加且连接未及时释放为什么:新索引上线后统计信息未更新为什么:迁移流程没有包含统计信息检查最后一层可能是流程缺口。修复应同时覆盖代码、配置、监控和发布流程。
8. 复盘
跳转到“8. 复盘”复盘目标不是追责,而是改变系统。至少包含:
- 时间线:何时发现、何时决策、何时恢复。
- 影响范围与持续时间。
- 直接触发因素和促成条件。
- 哪些监控有效,哪些缺失。
- 哪些操作成功,哪些浪费了时间。
- 可执行改进项、负责人和截止时间。
“加强培训”“提高意识”通常不可验证。更好的改进项是“为迁移加入统计信息检查”“为队列积压增加告警”“把回滚时间从 30 分钟降到 5 分钟”。
9. 性能与容量观测
跳转到“9. 性能与容量观测”延迟升高时,先分清:
- 请求量增加。
- 单请求变慢。
- 依赖变慢。
- 资源饱和。
- 排队或锁等待。
- 重试放大。
观察 CPU、内存、磁盘 I/O、网络、连接池、线程/协程数、队列长度和 GC。使用 profiler 找热点,不要只凭代码直觉。
容量测试要看业务链路和真实数据分布。健康检查接口的压力测试不能代表数据库查询、文件上传或复杂页面。
10. Runbook
跳转到“10. Runbook”每个关键服务应有简短、可执行的 runbook:
服务用途与依赖关键指标和正常范围常见故障与判断方法回滚与降级步骤数据恢复与联系方式升级路径runbook 要在故障前验证,而不是事故中第一次打开。命令、权限和链接过期会让文档在关键时刻失效。
检查清单
跳转到“检查清单”- 是否有用户视角的 SLI、SLO 和错误预算。
- 指标、日志、trace 是否通过 request ID 关联。
- 日志是否结构化、脱敏,是否有保留期。
- 指标标签是否控制基数。
- 仪表盘是否区分总览、业务和排障。
- 告警是否对应用户影响,是否有负责人和操作说明。
- 是否有多窗口错误预算和容量告警。
- 故障时能否快速回滚、降级和切换流量。
- 复盘是否产生可验证、可跟踪的改进项。
- 关键服务是否有经过验证的 runbook 和恢复演练。