告警是个细节游戏。没人会因为自己的意图被叫醒——叫醒你的是你亲手写下的那条查询语句。想睡个安稳觉,查询就得和你脑子里想的是一个意思。而大多数时候,你发现它不是那个意思,是在凌晨三点。
我见过最清晰的例子,甚至算不上隐蔽。一位同事给文件处理器配了一条消息处理量的告警,当作吞吐信号。听起来完全合理——如果一条消息等于一个工作单元的话。可惜不是。一条消息携带的是一批最多十个URL,而我们真正关心的是PDF下载量。
![]()
关键不在于这是个有意的近似。没人权衡过按下载次数埋点的成本,然后决定用消息数当便宜替代品。写规则的人压根不知道一条消息里装着多个URL,所以从一开始就不存在什么权衡。我们直接跟踪错了指标。
下游的一切都继承了这个问题。阈值、SLO、几个月积累下来的"这个数字正常吗"的直觉——全部悄悄用错误的单位计价。
五条消息,二十六个工作单元,两者之间没有恒定比例。
这就是下面每一条教训的形状。指标本身没问题。指标到意图之间的映射,才是断裂的地方。
教训一:下限告警分不清"卡死"和"没活"
最直观的吞吐告警是一个下限:M分钟内少于N个条目,就报警。在夜间批处理负载下,这条告警每晚必响,因为下限无法区分"卡住了"和"空闲,确实没活干"。
队列中最老消息的年龄,两个问题都没有。如果队列里没有东西,就没有东西会变老,安静时段自动静默。如果有工作排队但没在动,年龄就会增长——而且无论消费者是崩溃了,还是活着但就是不消费,年龄都会涨。后者是存活检查完全漏掉的场景。
它还省得你编一个数字。"每分钟40条消息正常吗?"需要你对自己的负载建一个模型。"有没有东西在这儿躺了六个小时?"则扎根于队列自身的排空时间,你在评审会上也站得住脚。
同一天,同一个事故。下限告警二十四小时里响了十七个小时;年龄信号只响了一次。
教训二:队列年龄告诉你"没在排空",不告诉你"为什么"
队列年龄告诉你工作没有排空。它不告诉你原因,而原因有两种截然不同的可能:
消费者死了,没人处理工作。或者消费者活着,但处理速度跟不上生产速度。
这两种情况需要完全不同的应对。第一种是重启或替换消费者。第二种是扩容或降速。如果你只知道"工作没排空",你连该叫醒谁都不知道。
这就是为什么年龄信号需要配套一个诊断信号,而不是替代品。
教训三:按单元计时的截止时间,计数比触发本身更有信息量
按单元截止时间能回答这个问题——"有没有任何单个任务超过了N分钟?"——而有用的信号不是它触发了,而是它触发了多少次。
想象两行数据:一行显示截止时间告警触发了一次,另一行显示触发了五十次。告警本身在两行里完全一样。是计数告诉你你处在哪种情况里。
触发一次,可能是偶发慢请求,不值得半夜爬起来。触发五十次,说明系统性问题正在蔓延,该行动了。
教训四:零容忍规则会把"慢"误报成"故障"
我们的API是第二种情况的干净版本。两周时间里,大约十个请求超过了10秒,其他一切都很快。两周流量里十个请求不是一次故障,而一条10秒零容忍规则会因为这些慢但不坏的请求把我们叫醒。所以截止时间被放到了超限真正罕见的地方——30秒——现在这条告警的意思是"有东西卡住了",而不是"有东西慢了"。
这个区别至关重要。告警应该意味着"有东西坏了",而不是"有东西不符合预期"。前者值得叫醒人,后者只值得记个日志。
核心洞察:指标没问题,映射才是问题
回到最开始的例子。消息处理量本身不是坏指标——它只是不能代表PDF下载量。问题在于写告警的人不知道一条消息等于多个URL,所以映射从一开始就是错的。
这引出一个实用的自检方法:对于每条告警,问自己"这个指标真的代表我关心的东西吗?"不是"它合理吗",而是"它和意图之间有没有一对一的映射关系"。如果没有,要么换指标,要么接受误差并明确写下来。
另一个自检:你的告警能区分"卡死"和"空闲"吗?能区分"慢"和"坏"吗?如果答案是否定的,你迟早会在凌晨三点被叫醒,然后发现查询语句和你脑子里的想法不是一回事。
告警的细节游戏,赢在写规则之前。赢在理解你的数据模型,理解你的工作负载,理解指标到意图的映射。否则,你只是在给自己写凌晨三点的闹钟。
特别声明:以上内容(如有图片或视频亦包括在内)为自媒体平台“网易号”用户上传并发布,本平台仅提供信息存储服务。
Notice: The content above (including the pictures and videos if any) is uploaded and posted by a user of NetEase Hao, which is a social media platform and only provides information storage services.