需通过外部监控系统(如prometheus)实时计算错误预算,核心是用rate()基于7天滚动窗口统计5xx错误率并与slo阈值比对,所有计数必须立即上报、避免依赖进程内状态,且错误定义应基于用户感知失败而非仅http状态码。

怎么在 python 服务里实时看到错误预算还剩多少
错误预算不是 Python 原生概念,得靠你主动把 SLO 指标和错误计数对齐。核心是:用一个共享的、带时间窗口的计数器,持续记录请求总数和错误数,再按 SLI 公式算出当前消耗率。
推荐用 prometheus_client 暴露两个指标:http_requests_total{status=~"5.."} 和 http_requests_total,再让 Prometheus 按 rate() 算 7d 错误率,和你的 SLO 阈值(比如 0.01)比对。别自己写滑动窗口——精度难控、内存泄漏风险高。
- 别用 Python 的
time.time()+ 字典手工维护 7 天数据:时区、进程重启、多实例聚合全会出问题 - 如果服务没接 Prometheus,至少用
statsd或datadog.statsd上报原始计数,后端做聚合 -
status标签必须区分 4xx/5xx;SLO 通常只计入 5xx,但得确认你的 SLO 定义是否包含 429 或 400
为什么用 rate() 而不是 count() 算错误预算消耗
错误预算是基于「滚动时间窗口」的速率型预算,不是总量。比如 SLO 是「99.9% 的请求在 7 天内成功」,那就要看最近 7 天的错误率,而不是从上线至今的累计错误率。
用 Prometheus 的 rate(http_requests_total{status=~"5.."}[7d]) / rate(http_requests_total[7d]) 才能逼近真实消耗速度。直接用 count() 会把历史冷数据拖进来,尤其服务刚上线或流量突降时,结果严重失真。
立即学习“Python免费学习笔记(深入)”;
- 避免用
irate():它只看最近两个样本,抖动极大,不适合预算这种需平滑判断的场景 - 确保抓取间隔 ≤ 30s,否则
[7d]窗口内样本不足,rate()返回空或 NaN - 如果用 OpenTelemetry,注意
Counter类型默认是累加的,要配View拆出 status 维度,不然没法过滤 5xx
Python 进程重启后错误计数不丢的关键配置
默认的 Counter 或 Gauge 在进程退出时就清零,但错误预算是跨进程生命周期的——你得让指标状态能被外部系统持续观测,而不是依赖单个 Python 实例的记忆。
唯一靠谱的做法是:不依赖进程内状态,所有计数都立刻上报。哪怕用文件临时落盘,也比内存计数强,因为文件可被外部轮询或日志采集器读取。
- 禁用
prometheus_client.MultiprocessCollector的默认行为:它用文件存每个进程的值,但汇总逻辑在 /metrics 接口里,若该接口没被调用,旧进程数据就永远卡在磁盘上 - 改用
pushgateway时注意:它不支持自动过期,必须手动发 delete 或设job+instance标签配合 TTL - 如果必须本地暂存,用
sqlite3写带timestamp的表,每次上报后删掉超过 7 天的行,别用 pickle 存 dict
常见错误:把 HTTP 500 当成唯一错误信号
真实服务里,500 只是冰山一角。超时、gRPC DEADLINE_EXCEEDED、数据库连接池耗尽、甚至 json 解析失败但返回 200,这些都会导致用户失败,却不会体现在 5xx 计数里。
错误预算消耗要看「用户感知失败」,不是「HTTP 状态码失败」。必须根据业务语义定义什么是“错误”:比如支付服务里,返回 {"code": "PAYMENT_FAILED"} 的 200 响应,就得计入错误预算。
- 在中间件里统一拦截响应体或返回值,用正则或结构化字段识别业务错误码,再打标上报
- 避免在装饰器里用
try/except Exception统计:会把开发调试的KeyboardInterrupt、SystemExit也当错误 - 如果用了 fastapi,优先用
Depends()注入的全局异常处理器,而不是每个路由写try/except
真正难的从来不是怎么记数,而是怎么定义“一次失败”。这个边界一旦划错,后面所有计算都是幻觉。