凌晨一点二十,客服值班群忽然弹出一条消息:“很多用户反馈下单失败,账单页一直在转圈。”紧接着你的手机亮了,是你领导打来的。接起来之前,你脑子里冒出的是那句老话:监控怎么没报警?
后来拉监控回放,错误率从21:07就开始异常了,整整一个多小时后,技术团队才被客服的通知喊醒。喊醒之后又是半小时定位,再花半小时处理,等线上恢复,已经是深夜两点多了。
这不是个别现象,而是大多数线上故障的常态——第一个发现故障的,往往不是值班工程师,也不是监控告警,而是用户和客服。标题里那句“线上崩了,谁先知道?”,答案很多时候就是“最不该先知道的那个人”。这篇文章我想聊聊,为什么监控系统总是慢半拍,以及怎么把“谁先知道”这件事从一个段子,变成一个真正可量化、可改进的工程问题。
1. 用户先知道故障,不是偶然,是必然
1.1 一条真实的故障时间线
我拿一个电商活动场景举例。晚上八点半,运营刚推送了一波活动消息,30分钟后订单量开始猛涨。到了21:07,数据库连接池被打满,一部分接口开始超时,用户在“提交订单”按钮上转圈30秒,然后看到失败提示。
这个时刻,用户会做什么?大部分人刷新一下,再试一次,不行就放弃。一小部分人会换到反馈入口,或者打客服电话。于是到了21:26,客服系统会话量已经是正常水平的两倍。客服组长在群里问一句:“技术这边是不是有问题?”
21:40,值班工程师被拉进群,翻监控面板,发现订单接口错误率已经跑到40%。21:47,才想到去看数据库连接池——水位已经100%。22:05,扩容加重启,22:21,订单量恢复。
这条时间线里,第一发现人是放弃下单的用户。第二个是客服。技术团队排在第三。而监控系统呢?它确实在21:07就已经采集到了错误率突变的数据,但在这之后的一个小时里,没有任何一条告警被发出来。原因说起来挺无语的——这个接口的错误率告警阈值,设的是80%。
1.2 为什么“用户先知道”几乎是一个必然结果
把这个案例拆开看,监控系统没在第一现场发现故障,通常不是监控软件不行,而是三件事没做到位。
第一,该看的指标没看。很多团队把监控重点放在CPU、内存、磁盘、JVM这些基础设施资源上,这类监控解决的是“机器活没活着”的问题,解决不了“业务还能不能用”的问题。机器活着不代表接口正常,接口返回200不代表下单成功。这是两个层面的事。
第二,看了,但阈值定得不合适。上面那个例子,错误率阈值设成80%才开始告警,是因为开发同学觉得“偶尔报几个错很正常”。这话本身没错,但“偶尔几个”和“系统性故障”怎么区分?一刀切的静态阈值,要么在业务高峰期频繁误报,要么在灾难面前安安静静。
第三,触达链路过长。就算告警发出去了,如果报警发到一个几十号人的大群里,没有明确的负责人,不响铃、不升级,那这条告警本质上等于没发。告警的终点必须是一个“人”——一个明确有响应义务、且不会在睡梦中错过电话的人。
我判断一件事是不是工程问题,就看它能不能被系统化地解决。“用户先知道”完全可以通过指标完善、阈值调优、触达升级来从根上改变。它不是宿命,是设计缺陷。
1.3 另一个容易被忽略的点:平均值会掩盖故障
插一个很有意思的情况。有时候监控是有数据的,但度量的方式不对。比如某个服务有十个实例,其中一个挂了,其他九个正常。平均值算下来,错误率只上升了5%,阈值设的是20%,所以没告警。但问题在于,挂了的那台恰好承载了某个特定区域、某个特定渠道的流量,那部分用户的错误率是100%。
用平均值去判断故障,相当于把十个人的健康状况加在一起除以十,只要不是全军覆没,数字看起来都还行。真实场景里,我的做法是对关键接口的错误率和响应时间按实例、按区域、按用户分组观察,并且用分位数来盯。平均值是给报表看的,分位数才是给告警用的。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 监控告警的三道关卡:采集、检测、触达
想改变“用户先知道”的现状,得先把告警链路拆开。一条告警从产生到被人看到,至少经过采集、检测、触达三道关卡,每一关都有可能导致它变成一个沉默的废报。
2.1 采集层:指标分层,先把“真相”收上来
监控指标大致可以分成三层:
| 层级 | 典型指标 | 主要用途 |
|---|---|---|
| 基础设施层 | CPU、内存、磁盘、网络、JVM GC | 容量规划、健康度面板 |
| 应用层 | QPS、错误率、响应时间、线程池、连接池 | 故障告警 |
| 业务层 | 下单量、支付成功率、订单创建到支付耗时、搜索无结果率 | 用户体感告警 |
三层里面,离用户越近的,越适合做告警。基础设施层适合做趋势观察和容量规划,应用层适合做服务故障告警,业务层才是用户体感的直接映射。
这里有个非常常见的坑:平台已经接入了整套基础设施监控,但对业务故障的感知能力几乎为零。机器健康、业务已挂的场面,经历过一次就懂了。所以我把这条放在采集层的第一课:基础设施指标不是不重要,而是它们永远替代不了业务指标。
另外一个采集层的坑是探活不等于可用。之前维护过一个老系统,健康检查接口返回200,但从监控数据看,进程内的线程池已经被长时间占满,大量请求在排队。接口虽然能返回成功,响应时间却从50毫秒涨到了3秒。HTTP探活依然显示“通过”,因为探针只检查了“能不能访问”,没有检查“访问之后花多久”。这种健康检查,做的是形式主义。
2.2 检测层:阈值的设定逻辑,直接决定告警的可信度
先泼一盆冷水:静态阈值虽然简单,但在业务波动大、周期性强的系统里,非常容易失真。凌晨两点,Redis慢查询半小时出现一次,大概率不用管;同样频率出现在下午的抢购时段,可能就意味着缓存击穿。用一条平直的线去衡量一天24小时都在变化的业务,本质上对这个业务是不公平的。
想解决这个问题,有几个方向可以参考:
- 动态基线:按小时维度统计历史同期的波动范围,当当前值超过基线的若干倍标准差时再触发告警。这种方式对有明显周期性波动的系统效果很好。
- 环比和同比:和5分钟前比,和昨天同一时刻比。故障通常表现为“相对于正常状态突然变化”,所以环比往往比绝对阈值更灵敏。
- 多指标联合检测:单看错误率可能有噪音,但“错误率上升”同时伴随“QPS骤降”和“依赖组件超时”,那基本就是故障了。多条件用AND组合,误报率能降不少。
再放一个实际的告警规则示例,以Prometheus规则为例,目标是“订单创建失败率超过5%且持续3分钟”才告警:
yaml复制groups:
- name: business-alerts
rules:
- alert: OrderCreateFailureRateHigh
expr: |
(
sum by (app) (rate(order_create_failed_total[5m]))
/
sum by (app) (rate(order_create_total[5m]))
) > 0.05
for: 3m
labels:
severity: p1
annotations:
summary: "订单创建失败率超过5%,持续3分钟"
for: 3m 这个参数很关键,它要求条件持续3分钟才触发告警,能过滤掉不少瞬时抖动。但如果把3分钟改成30分钟,用户体验到的故障时间就已经拉长到无法接受了。这个值需要根据业务容忍度去调。
还有一个教训我记得很深。阈值不是设完就不动了。之前一个团队把GC停顿时间告警阈值设成50毫秒,结果一周收到几百条告警,最后有同事直接把这个规则屏蔽了。但恰恰在屏蔽后的第三天,Full GC导致大面积请求卡顿,没人第一时间发现。所以阈值设置必须遵循“宁可初期多调,不能长期无人维护”的原则。告警规则是活物,不是写完就交付的静态配置。
2.3 触达层:告警送不到“有响应义务的人”那里,等于没报
告警的触达,核心不是渠道多不多,而是责任明不明确。一个成熟的告警触达设计基本是三段式:
- 第一段,IM机器人推送到专项值班群,适合P2及以下级别告警。
- 第二段,短信、电话到当值人,适合P0、P1,或者IM在N分钟内未被确认的告警。
- 第三段,升级到值班组长,甚至更高负责人,适用于长时间无人处理的情况。
我特别想强调确认机制。很多团队把告警往群里一推就算完事,但“发出告警”和“告警被处理”之间有一大段距离。好的告警流程应该有ack(确认)和resolved(关闭)两个状态流转:告警触发后,当值人需要在约定时间内确认,否则自动升级。这样每条P0、P1告警都有明确的负责人和处置时效。
有个细节顺嘴提一下:别把所有告警都用电话轰炸。我们之前有一个规则,错误率超过5%就打值班电话,结果有一天晚上业务做压测,电话响了一整夜。从那以后,我们对电话告警的准入标准提得很高——只有“核心链路持续不可用”或者“资金链路受损”才允许走电话。电话告警是稀缺资源,消耗的是人的精力和应急状态的调节能力,它只能用在大事上。
3. 告警泛滥,比没有告警更可怕
如果说漏报是慢性病,那告警泛滥就是急性病,而且它最后也会变成慢性病。
3.1 告警风暴的真实场景:一个根因,全家跟着报警
继续用数据库连接池打满的例子。核心库连接池满掉之后,下游几十个微服务的数据库操作全部超时,每个服务都有自己的错误率告警规则,于是瞬间触发了200多条告警。群里刷屏,值班人的手机不停震动,真正重要的那一条——“数据库连接池水位超过90%”——反而被淹没在这堆消息里。等人反应过来,20分钟已经过去了。
你没法要求值班人在这种环境下准确挑出根因告警,所以系统得先替人做一层粗加工,常用的手段有三个:
- 告警聚合:相同服务、相同告警规则在一定时间窗口内只产生一条,后续重复触发只更新持续时间和影响次数。
- 依赖抑制:当下游依赖(数据库、消息队列、网关)已经触发高优告警时,抑制上游服务的同类告警,减少全家桶刷屏。
- 告警分组:把同一时间窗口、同一链路上的相关告警聚合到一个事件里,值班人打开事件,看到的是因果图,不是告警列表。
告警风暴不只是体验问题,它会导致真正的根因告警被漏掉,进而让一个本来5分钟能定位的问题变成半小时起步。这个损失非常具体。
3.2 告警疲劳:为什么工程师习惯了忽略红色数字
心理学里有个概念叫习惯化——当一个人反复暴露在同一种刺激下,他对这个刺激的反应会越来越弱。告警系统也一样。如果值班群里每天有几十条无关紧要的告警,工程师会很快学会不看群、静音群、屏蔽机器人。然后,等真正的事故降临,刚好没人看,于是线上崩了,客服先知道了。
重建信任只能靠两件事:降低噪声,提高准确率。降低噪声靠收敛和治理,提高准确率靠规则设计,缺一不可。
有段时间我们团队每周做一次“告警回顾”,把过去七天所有触发过的告警拉出来过一遍,标记有效、无效、可合并三种状态。有效告警保留,无效告警排查原因,可合并的合并同类项。这个动作看起来简单,但坚持三个月后,规则库从180多条降到了60多条,同期线上故障的平均发现时间反而缩短了一半。规则少了,每一条的权重就高了,值班人就更愿意认真对待。告警系统的价值不在于规则多,而在于每一条都有人信、有人看、有人处理。
3.3 不要把“紧急”和“重要”混为一谈
告警分级如果设计得不好,最常见的表现就是所有告警都是最高级——结果没有告警有分量。这里给一个按“影响面”而不是“技术表象”划分的参考:
| 级别 | 定义 | 响应要求 |
|---|---|---|
| P0 | 核心业务不可用,资金链路受损 | 立即响应,立即升级,电话通知 |
| P1 | 重要功能大面积异常,服务降级,影响部分用户 | 15分钟内确认,持续处理 |
| P2 | 非核心功能异常,或有明显规避手段 | 工作时间处理 |
| P3 | 信息性告警,不影响用户 | 定时集中看 |
关键点在于,分级标准要写清楚、成文,并且让每个值班人都理解。分级标准不一致的团队,经常在深夜遇到故障时,先争论十分钟“这个到底算不算P0”,而不是动手处理。级别是完全可以事后复盘调整的,但不能在响应现场反复拉扯。
4. 从“等着用户报障”到“系统先知道”的落地路径
前面讲了这么多问题,最后还是得落到怎么做。这一章我说点能直接用的建设顺序和日常维护方法。
4.1 可观测性的地基:日志、指标、链路追踪,一个都不能少
监控告警不是孤立存在的,它建立在可观测性三件套之上:
- 日志:要结构化,要带trace_id,才能把告警事件和具体请求对应起来。
- 指标:时间序列数据,用于趋势判断和阈值检测。
- 链路追踪:把一次请求经过的所有服务串起来,用于告警触发后的快速定位。
这三者之间的关系是互相配合的。告警解决“哪里出了问题”,链路追踪解决“这个问题影响哪些请求”,日志解决“这个请求到底发生了什么”。
我见过不少团队,日志写得非常随意,没有trace_id,也没有统一的字段规范。等告警响了,人进去查问题,发现日志根本无法串联出完整链路,排查时间被无限拉长。监控告警只是“发现”环节,要真正缩短故障恢复时间,后面的排查工具链必须同步跟上。地基不打牢,告警做得再花哨,也只是在一个松软的沙堆上盖楼。
4.2 告警规则的正确姿势:少而准,而不是多而全
很多团队以为监控能力等于告警规则数量,这是错的。规则越多,意味着需要维护的边界越多,误报的可能性越高,值班人的认知负担越大,被忽略的概率也越大。我个人比较推荐这几个方向:
- 少设技术指标告警,多设业务指标告警。技术指标更适合做面板,业务指标才直接反映用户体感。
- 少用静态阈值,多做动态基线。基线的维护成本高一点,但长期来看比阈值更省心。
- 少看单个指标,多联合多个指标判活。联合成组能过滤掉大量噪声。
还有一条:新增一条告警规则的同时,就必须为它写处置SOP。没有SOP的告警是假告警,人收到了也不知道该干嘛,最终还是会沦为噪声。我见过最理想的状态是,一条告警文本里除了说明“哪里出问题”,还带一个“建议排查入口”的链接,值班人点进去就能开始干活。
4.3 演练是唯一能让告警链路真正可信的手段
规则和配置写得再漂亮,没有实际验证过,都不能保证它在故障时真的会响。我建议每季度至少做一次告警链路演练。具体操作可以挑一台非核心机器,人为模拟一个真实故障——比如把某个服务的连接池数量调到几乎为零,或者手动把某个接口的错误率拉高——然后观察告警是不是按预期触发、有没有到达当值人、SOP好不好用。
第一次做演练的时候,几乎一定能发现三五个问题:规则名写错了、触达被群设置为免打扰、联系人已经离职、告警阈值被之前压测的残留数据调得太高。这些问题不通过演练暴露出来,就会一直隐藏到真实事故发生的那一刻,然后变成复盘记录里的一个刺眼条目。
4.4 值班机制的最后一公里:从发现到恢复
告警只是整个响应循环的开头,后面还有定位、止损、恢复、复盘四个阶段。定位阶段依赖日志、指标、链路追踪是否完备;止损阶段依赖预案——你是否知道该降级哪个开关、重启哪个集群、扩容哪个模块。我建议针对核心系统提前整理一份“故障响应手册”,把常见故障的症状、快速定位路径、止血动作写清楚,给新手值班人兜个底。
还有一个值得坚持的习惯:每次故障恢复后,都要回答一个问题——“这次故障,监控为什么没有更早发现?”不是做错题集,而是为了找到那个具体断点:是采集缺失,还是阈值不合理,还是触达链路断了。每次补上一个断点,下一次故障的发现时间就会快一点。
开头说的那次故障,后来复盘出来的结论就是三条:业务指标没接入告警、错误率阈值过高、值班确认机制缺失。一个月内,我们把这三件事都补上了。再后来一次高峰期故障,监控在用户感知之前15分钟就发出了告警,值班人确认后直接定位到缓存集群,止损时间从小时级压缩到了分钟级。
这个过程靠的不是某款监控产品,而是把“谁先知道”当成一个工程问题来对待。如果你所在的团队,现在还是靠客服群通知才知道线上出故障,那么今天就可以做一件事:把最近一次故障的完整时间线拉出来,标出每个环节是隔了多久、由谁发现的,然后把那些“本可以由监控发现”的时间点找出来,看看断点到底在哪。补上它,下一次你就不用在凌晨一点半接到那通电话了。
