压测演成的事故
大促前两周,老王组织全链路压测,第一轮就翻车:模拟发号服务实例宕机,预期是流量切到存活实例、下游无感,实际是下单链路整体报错——发号 SDK 的重试没有超时上限,连接池被拖死,错误像瘟疫一样传遍调用方。复盘会上 DBA 说得扎心:号段、雪花、基因都做了,唯独没人问过「发号这一环挂了,业务怎么活」。这一篇补上这门课。
先看清单点在哪
发号链路上有三个可能死的点,逐个加固:
服务实例:发号服务多实例部署、前面挂负载均衡。雪花模式天然无状态,实例随便加——workerID 分配好就行(第 8 篇);号段模式的实例也无需同步,各领各的号段。这一层的冗余最便宜,必须做满。
存储层:号段库挂了才是真麻烦——所有实例的 buffer 迟早耗尽。DB 主从半同步打底,预算足的上多机房;Redis 发号(第 9 篇)同理,哨兵或集群模式起步。雪花模式没有这一层,这也是它高可用的先天优势。
客户端缓冲:其实第 4 篇的双 buffer 就是第一道降级——本地囤着千号,DB 抖三秒下游无感。压测翻车后老王把 SDK 改了:号段取不到时先用尽本地存量再报错,并把重试超时从「无限等」改成「快速失败 + 退避」。
降级阶梯:一级一级往下走
高可用的核心思想不是「不挂」,而是挂了之后每一层都有下一步:
第一级:DB 抖动——本地双 buffer 顶住,秒级到分钟级无感。监控 buffer 水位,水位过低告警。
第二级:DB 长时间不可用——号段耗尽在即,切备库或第三机房号段库(号段表的 max 值在备库是延续的,切换后新号段不重叠;这里再度呼应第 3 篇的半同步——异步复制的备库切换会号段回跳,对账逻辑要备好)。
第三级:发号服务整体失联——启用本地兜底发号:业务应用内置一套精简雪花(或预置备用号段),应急开关打开后本地发号。兜底 ID 必须能和正常 ID 区分——比如用时间戳高位的一个保留位做标记,或者落到独立号段区间。为什么必须区分?兜底期间发的 ID 在有序性、安全性上都是降级品,事后要能圈出来做对账与补录。
降级的每一级都要提前想好「什么时候降、谁来拍板、怎么升回来」,写成操作手册。最忌讳的是临时找方案——凌晨三点没人有心情读你的架构文档。
大促专项:容量与预热
大促流量是平时的几十倍,发号侧两个动作:一是容量预估——按峰值 QPS 倒推号段长度,把 step 提前调大(Leaf 的动态 step 会自己适应,但预留量要人工审一遍);二是预热——应用发布或扩容后,新实例的 buffer 是空的,第一波流量全打到 DB,大促前把所有实例的 buffer 预填充,别让新节点在开局就当短板。
监控四件套
| 指标 | 看什么 | 异常信号 |
|---|---|---|
| 号段消耗速率 | 业务增速与突发流量 | 速率陡增,号段即将提前耗尽 |
| buffer 水位 | 本地存号还能撑多久 | 水位低于安全线,预加载失败 |
| 时钟偏移 | 雪花集群 NTP 状态 | 偏移超阈值,回拨风险预警 |
| 发号延迟 P99 | 下游体验 | P99 飙升,DB 或网络异常前兆 |
最后一条纪律:降级预案必须定期演练。老王把「杀发号实例」「断号段库」「全服失联」三档场景进了季度演练排期,每次演练记录切换耗时与业务影响。预案写在文档里是作文,演练进排期表才是能力。
小结
高可用一句话:实例冗余解决机器挂,双 buffer 解决存储抖,降级阶梯解决整体瘫,兜底 ID 打标可追溯,演练进排期才算数。至此发号器的工程闭环全部搭完:从原理到选型到安全到分片到容灾。但还有一批坑散落在边边角角——位数分配的算术题、时间戳的过期倒计时、号段长度的心理战。下一篇把踩坑实录一次清算。
评论 (0)