后端系统工程 36:指标采集与告警生命周期——缺数据不能算恢复
本文属于《后端系统工程:从接口契约到故障恢复》系列。文中提到的演练工程、测试与实验证据位于博客源码仓库的 batch-4/ 目录(examples/、evidence/ 等子目录);业务数据均为合成数据。 告警系统一次只回答一个问题:现在是否需要叫人。一条规则从采样到通知要经过五个环节:样本是否到达、条件是否持续、多条告警怎样合并、哪些应当暂时不发、什么时候可以宣布恢复。任何一环把“没有数据”当成“数据正常”,值班人员都会在故障还在的时候收到一条恢复通知。 本篇用一个离散时钟模型实现这条链路,覆盖数据缺失、触发、重复抑制、静默、抑制、恢复和规则变更,并用负向对照证明错误做法确实会出错。模型借用 Prometheus 与 Alertmanager 的概念命名,行为以本文契约为准;凡是与官方实现有差异的地方,正文单独说明。 1. 需求与不变量 监控对象是一个合成的 checkout 服务,两个实例 c1、c2,每 15 秒采集一次 error_ratio(错误请求占比)和 up(采集是否成功)。告警规则有两条: 规则 条件 for keep_firing_for 缺数据宽...
后端系统工程 37:超时预算与负载保护——慢下游结束之后系统为什么还起不来
本文属于《后端系统工程:从接口契约到故障恢复》系列。文中提到的演练工程、测试与实验证据位于博客源码仓库的 batch-4/ 目录(examples/、evidence/ 等子目录);业务数据均为合成数据。 一个下游变慢 20 秒,上游在这 20 秒里失败,这是预期之内的损失。更难接受的是另一种结果:下游已经恢复,上游的成功率仍然是零,并且一直保持下去。本篇用同一份合成负载比较四种超时与重试策略,测量请求放大、拒绝、积压和恢复时间,并解释“慢下游结束之后系统起不来”是怎样被重试和无界队列维持住的。 实验是确定性的离散事件仿真。它能比较策略之间的相对行为,不能给出任何真实服务的吞吐或延迟。 1. 需求与规模假设 调用链为 客户端 → gateway → svc_a → svc_b。 项 假设值 说明 客户端到达 泊松,平均 400 次/秒,持续 60 秒 固定随机种子 客户端截止时间 1 秒 用户等待上限;超时后客户端最多再试 2 次,间隔 50 毫秒 svc_b 并发槽位 16 平均服务时间 20 毫秒,指数分布;理论容量约 800 次/秒 慢下游窗...
后端系统工程 38:数据备份、恢复与地域切换——恢复演练要检查数据,不只检查进程
本文属于《后端系统工程:从接口契约到故障恢复》系列。文中提到的演练工程、测试与实验证据位于博客源码仓库的 batch-4/ 目录(examples/、evidence/ 等子目录);业务数据均为合成数据。 “备份成功”是一条日志,“能恢复到指定时刻且数据正确”才是一项能力。两者之间隔着几个容易略过的问题:恢复点到底落在哪里,归档的 WAL 是否完整,恢复出来的实例里账是否平,角色和权限是否还在,切换之后旧主库会不会继续接受写入。 本篇在一台机器上启动多个隔离的 PostgreSQL 16 实例,实际执行基础备份、WAL 归档、按命名还原点的时间点恢复(PITR)、计划内切换和注入复制中断的非计划切换,每一步都用 SQL 检查数据本身。所有演练证明的是脚本与步骤在单机上可执行、检查项能发现错误;它们不证明跨地域网络、存储、DNS 或权限体系的行为。 1. 需求、目标与不变量 被保护的数据是一个合成的双边记账库:account(id, balance) 有 200 个账户,entry(request_id, account_id, amount, batch) 记录每笔转账的借...
后端系统工程 39:综合方案评审与交付——沿四条路径检查跨模块保证
本文属于《后端系统工程:从接口契约到故障恢复》系列。文中提到的演练工程、测试与实验证据位于博客源码仓库的 batch-4/ 目录(examples/、evidence/ 等子目录);业务数据均为合成数据。 单个模块的测试证明的是模块自己的承诺:消息服务保证同一个客户端请求只产生一条消息,队列保证至少投递一次,通知服务保证按幂等键推送。用户看到的结果却取决于这些承诺怎样接在一起:消息已经撤回,通知还会不会把正文推出去;队列重投一条事件,用户会不会收到两次推送;配置回退之后,正在进行的撤回按哪个窗口判断;通知服务停了十分钟,恢复后积压能否排空、告警是否如实触发和恢复。 本篇的评审对象是一个专门构造、范围受控的合成系统 Relay,有明确的接口契约和不变量,并沿发送、撤回、配置回退、积压恢复四条路径写成可执行的验收用例。Relay 不是系列 04–35 章实现的汇总。本批次没有取得那些章节的实现,本文不声称审阅或验证了它们;它们与 Relay 的对接点在第 9 节逐项列为待集成项。 1. 评审对象 模块 职责 持久状态 消息 发送、撤回、历史查询 message、c...
后端系统工程 E01:在线状态与分层心跳——一个带误差上界的观察结果
本文属于《后端系统工程:从接口契约到故障恢复》系列。文中提到的演练工程、测试与实验证据位于博客源码仓库的 batch-4/ 目录(examples/、evidence/ 等子目录);业务数据均为合成数据。 “在线”看起来是一个布尔值,实际上是服务端对远端设备的一次推断。设备离开的方式不止一种:主动关闭连接、进入隧道导致连接半开、所在的接入网关整机宕掉。每一种方式被服务端察觉的路径不同,耗时也不同。服务端能承诺的不是“状态永远正确”,而是“从设备事实上离开,到对外发布离线,最多经过多久”。 本篇用受控时钟实现三层心跳:连接层探测、网关到存储的租约续期、用户级视图的去抖发布,并为每种离开方式推导和测量误差上界。隐私粒度作为同一视图的另一个维度一起处理。 1. 需求与规模假设 用户可以有多台设备;任一设备在线,用户即在线。 订阅者只看到被允许的粒度:精确(在线/离线 + 分钟级最近在线)、粗略(在线/最近/很久以前)、隐藏(未知);非联系人与被拉黑者什么都看不到。 每种离开方式都有可计算的“发现离线”上界;正常心跳偶尔丢一次,不应导致误报离线。 设备换网关重连时,旧连接迟到的下...
后端系统工程 E02:定时调度与日历语义——“每天 09:00”不是每 24 小时一次
本文属于《后端系统工程:从接口契约到故障恢复》系列。文中提到的演练工程、测试与实验证据位于博客源码仓库的 batch-4/ 目录(examples/、evidence/ 等子目录);业务数据均为合成数据。 第 19 篇处理的是单次到期任务:一个确定的 UTC 时刻,到了就领取执行。日历调度多了一层翻译:用户说的是“柏林时间每个工作日 07:00”“每月最后一天”“每月 31 日”,系统要把这些本地时间规则翻译成 UTC 时刻,还要回答几个翻译中会遇到的问题:本地时间不存在怎么办,出现两次怎么办,2 月没有 31 日怎么办,调度器停机三天后恢复要不要补跑。 本篇实现一个 RRULE 子集的展开器和一个基于 SQLite 的调度器,用真实的 IANA 时区规则(本机 tzdata 2026c)验证夏令时跳变、半小时跳变、整天跳过、重复触发和补跑。 1. 需求与不变量 R1 规则以“本地名义时间 + IANA 时区名”保存,执行时刻按需展开,不预先存储 UTC 列表。 R2 跳变日的处理有明确策略,并在实例上留下注记,方便事后解释。 R3 同一个实例 (schedule_id,...
后端系统工程 E03:大群历史的冷热分层——归档之后,删除还要能追上去
本文属于《后端系统工程:从接口契约到故障恢复》系列。文中提到的演练工程、测试与实验证据位于博客源码仓库的 batch-4/ 目录(examples/、evidence/ 等子目录);业务数据均为合成数据。 大群的历史消息有一个典型的访问形状:最近几天的消息被反复翻看,几个月前的消息很少有人读,但必须保留、能检索,并且在用户删除或撤回之后不能再被读到。把旧消息搬到更便宜的存储上,可以大幅降低成本;问题在于搬动本身是一个多步骤操作,任何一步都可能中断,而搬走之后,删除、撤回这些“对历史的修改”还要能追上去。 本篇实现一个热层(SQLite 表)加冷层(按会话、按序号区间切分的压缩段文件)的历史存储,验证归档的中断恢复、跨层分页、段文件损坏检测、删除传播与撤回叠加,并用一个参数化的成本模型说明分层阈值怎样随访问分布变化。 1. 需求、不变量与规模假设 H1 历史分页:按 seq 从新到旧翻页,跨越冷热边界时不重复、不遗漏。 H2 归档在任意步骤中断后可以重跑,读路径在中断期间仍然正确。 H3 冷层数据损坏能被读路径发现,并以错误返回,不能返回错误的内容。 H4 删除对读路径立即...
后端系统工程 E04:消息通道替换与迁移——迟到事件与回退边界
本文属于《后端系统工程:从接口契约到故障恢复》系列。文中提到的演练工程、测试与实验证据位于博客源码仓库的 batch-4/ 目录(examples/、evidence/ 等子目录);业务数据均为合成数据。 替换一条在用的消息通道,比如把自建队列换成托管服务,或者在两个集群之间搬迁,不能靠“周五晚上停写、切换、周一观察”。切换的那一刻,旧通道里还有在途事件,新通道刚开始接收的事件可能比旧通道里的同一事件先到;切换之后,旧通道还会因为长尾延迟吐出几条早该到达的事件。迁移方案要回答四个问题:怎样证明新通道可以接手,切换时同一事件来自两条通道怎么办,切换后旧通道迟到的事件怎么办,以及到什么时候为止还能无损地退回去。 本篇用离散事件模型实现一个四阶段迁移控制器,在同一条合成事件流上注入延迟、重复、丢失和写入失败,验证副作用恰好一次,并用负向对照说明去重窗口和补漏期为什么不能随意缩短。 1. 需求与不变量 事件流是合成的业务通知事件:200 个业务键,每秒 50 个事件,每个事件带全局唯一的 eid、所属 key、在 key 内单调递增的 version 和一个随机载荷。消费者对每个事...
后端系统工程 E05:幂等导出与断点续传——同一个请求,同一份字节
本文属于《后端系统工程:从接口契约到故障恢复》系列。文中提到的演练工程、测试与实验证据位于博客源码仓库的 batch-4/ 目录(examples/、evidence/ 等子目录);业务数据均为合成数据。 “导出我的聊天记录”看起来只是一次查询加一次下载,实际包含几个各自独立的问题。用户点了两次按钮,是一个任务还是两个;导出期间又有新消息、有人退群、有消息被撤回,文件里该有什么;导出跑到一半进程被杀,重跑出来的文件和不中断时是否一致;下载到 80% 断网,能否从断点继续,断点之后的字节是否属于同一个文件;下载链接被转发出去,别人能不能用。 本篇用 SQLite 和一个真实监听本机端口的 HTTP 服务实现这条链路,每个问题对应一组用例。全部数据为合成数据。 1. 需求与不变量 X1 同一用户、同一幂等键、同一请求体只产生一个导出任务;同键不同请求体被拒绝。 X2 导出内容由创建时刻的数据快照和权限快照决定,之后的写入、撤回、成员变化不改变结果。 X3 任务可以在任意分片处中断并重跑;重跑得到的产物与不中断时逐字节相同,已完成且校验通过的分片不重写。 X4 下载支持断点续传...
后端系统工程 E06:配额与多租户公平性——先定义指标,再比较调度
本文属于《后端系统工程:从接口契约到故障恢复》系列。文中提到的演练工程、测试与实验证据位于博客源码仓库的 batch-4/ 目录(examples/、evidence/ 等子目录);业务数据均为合成数据。 多租户共享一组工作者时,“公平”常常被说成一个直觉:大租户不能把小租户挤死。要比较方案,就得把直觉换成可以计算的指标:每个租户应得多少,实际得到多少,偏离多少算不公平,小租户的延迟是否受大租户突发的影响,空闲容量会不会因为限额而白白浪费。 本篇先给出这些指标的定义,再在同一份不均匀的合成租户流量上比较四种准入与调度方案:全局先进先出、每租户令牌桶配额、赤字轮询(DRR),以及 DRR 加配额。仿真只用于比较调度算法,不代表任何真实服务的容量。 1. 场景与规模假设 项 值 工作者 8 个,任务服务时间服从均值 10 毫秒的指数分布,理论容量约 800 个/秒 heavy 租户 平时 400 个/秒;20–40 秒突发为 5 倍,即 2000 个/秒 mid0–mid3 各 60 个/秒 small00–small19 各 5 个/秒 运行时长...















