符合统计条件的请求被分成成功事件与失败条件的并集。

错误预算为允许的失败命名

“可靠性达到百分之九十九点九”还不是一项完整的工程约定。它针对哪些用户、在哪个位置测量、观察多长时间,又把怎样的结果视为成功?只有回答这些问题,错误预算才能把允许的失败变成可计算的数量。因此,首先要定义统计总体和成功规则,而不是先挑选仪表盘上的绿色阈值。两个页面展示相同的百分比,如果分母和判定不同,也不能直接比较。 统计真正承诺的用户体验 服务水平指标描述实际测得的量,服务水平目标则为它规定阈值与时间窗口。对于基于事件的指标,成功比例等于符合条件的成功事件数除以全部符合条件的事件数。成功可以要求一次业务操作正确完成,并且没有超过延迟上限。仅有正常的响应状态码,不能证明订单已经正确写入;服务器内部计时也可能没有包含用户实际经历的网络等待。 分母必须明确,例如某个接口收到的有效客户请求尝试,而文档约定的合成探针可以另行统计。还要事先决定重试、客户端取消、认证失败、超时以及异步完成如何表示。排除条件应来自服务承诺的边界,不能在事故后临时创造,用来美化结果。应保留足够的记录,分辨一条事件是按规则排除,还是因为采集失败而没有进入统计;这两者的含义完全不同。 一条失败可以同时触发多个条件 假设成功请求既要返回预期响应,也要在三百毫秒内完成。一个缓慢的服务器错误同时违反两个条件,但仍然只是一条不良请求。应计算失败条件的并集,或者直接对成功判定取反。如果把“超时延迟计数”和“错误状态计数”相加,而两者不是互斥类别,就会重复计入交叉部分,导致预算消耗被夸大。 对于目标值与符合条件的请求总数,允许失败数就是请求数乘以一减目标值,剩余预算再减去实际失败数。请求型预算统计的是事件,时间型预算统计的是服务处于指定状态的时长。三十天内百分之九十九点九的时间型目标,对应四十三点二分钟的不良时间;不能把这个换算直接套到任意流量下的请求统计。部分用户失败、高低峰切换和不同请求权重,都会破坏两种口径之间的简单对应关系。 坏掉的样子 只统计应用服务器日志,可能看不到到达应用之前就被拒绝的请求。只记录已经完成的请求,也可能遗漏仍然卡在队列中的请求。这样得到的成功率可以很好看,用户却已经无法使用服务。需要选择合适的观测位置,纳入超时结果,并展示覆盖程度或独立检查;不能把遥测缺失解释成服务成功,更不能靠丢掉失败样本让分母看起来干净。 如果窗口里没有符合条件的事件,成功比例没有可解释的分母,应标记为没有观察值,并另行处理监控缺失。请求量很小时,高目标也会对单次失败特别敏感,因为预算是一个数学额度,不一定恰好等于整数次事件。不要为了得到一个“至少允许失败一次”的结果而向上取整。跨实例比较时也必须保留同样的范围和请求权重,直接平均各实例的百分比会改变指标含义。 快速验证 把代码保存为 slo-population.py,用 Python 3 执行。这个模拟接口的规则是:只有状态二百且耗时不超过零点三秒才算成功。样本包含已完成的客户事件,以及明确标记为探针的独立事件,用来验证分类计算。它没有证明真实采集器的完整性,也没有把正常状态码等同于真实业务结果已经正确完成。 from fractions import Fraction # Synthetic events: (kind, status, duration_seconds). events = ([('user', 200, 0.10)] * 9970 + [('user', 200, 0.50)] * 10 + [('user', 503, 0.02)] * 10 + [('user', 503, 0.50)] * 10 + [('probe', 503, 0.50)] * 100) eligible = [(status, duration) for kind, status, duration in events if kind == 'user'] bad_status = sum(status != 200 for status, _ in eligible) too_slow = sum(duration > 0.30 for _, duration in eligible) bad = sum(status != 200 or duration > 0.30 for status, duration in eligible) good = len(eligible) - bad target = Fraction(999, 1000) allowed = len(eligible) * (1 - target) remaining = allowed - bad assert len(eligible) == 10000 and bad_status == too_slow == 20 assert bad == 30 and bad_status + too_slow == 40 assert allowed == 10 and remaining == -20 assert Fraction(good, len(eligible)) == Fraction(997, 1000) print(f"eligible={len(eligible)}; good={good}; bad_union={bad}") print(f"status_failures={bad_status}; slow={too_slow}; wrong_sum={bad_status + too_slow}") print(f"SLI={good / len(eligible):.3%}; allowed={float(allowed):.0f}; remaining={float(remaining):.0f}") 样本里有一万次符合条件的客户事件,另有一百次探针被排除。客户事件中,状态不符合预期的有二十次,延迟超限的也有二十次,其中十次同时属于两类。因此失败并集实际只有三十次,成功事件为九千九百七十次;直接相加会错误地得到四十次失败。 ...

九月 17, 2026 · 1 分钟 · XB Field Notes