实测超时预算:每一跳固定 1000 ms 时 C 为 74 个已放弃的请求白干;传播剩余预算后 C 干活时间从 123.6 s 降到 73.9 s;重试 3 次会吃掉预算(120 次对 80 次调用);共用剩余预算的成功率 98.2% 对平分的 87.5%;排队超过截止时间就丢,成功数 34 → 162。
实测重试的放大:C 完全不可用时每多一层“试 3 次”C 收到的调用乘以 3(4 层 81 倍);C 偶发失败 90% 时 3 层各试 3 次 C 要多接 9.88 倍;容量有限时触发那一秒 C 收到 1987 次对 120 次而成功率没变;nginx 加 http_503 后 client 3 × nginx 2 × 服务 3 = 18 倍。
实测四种重试退避算法:200 个 client 同时被拒,无抖动的指数退避一波一波地同时到,完成时间中位数 19.25 s;Full / Equal / Decorrelated Jitter 把请求总数降到 1066 / 1034 / 822(固定间隔 2667);base 与 cap 的取舍。
实测重试预算:服务完全不可用时,不限的 3 次重试放大 3.000 倍,预算 10% 只有 1.099 倍;失败率 20% 时预算 10% 成功 88.3%、预算 20% 96.0%、不限 99.3%;三层叠加从 27.00 倍降到 1.31 倍。
手写熔断器与 Go 的 gobreaker v2.4.0 在同一剧本下的状态转换序列完全相同(10 次对 10 次);半开放行 1/3/10 个时服务不可用期间多挨的请求是 2/6/18;下游成功率 50% 时半开放行 10 个超过 599 s 都关不上。
实测熔断的窗口与阈值:计数窗口 10 个失败请求就触发(500 次/秒时 0.02 s,0.5 次/秒时 18 s),时间窗口约 5 s 触发且低速率下永不触发;小窗口加低阈值 6 小时误触发 328 次,时间窗口 0 次;只看失败率看不见只慢不错的下游。
实测熔断、超时与重试配错的四个案例:重试放在熔断器外面时下游收到 5 次调用对里面的 15 次;超时 3 s 时 1 个线程要 15.01 s 才打开;熔断器打开后还退避重试白等 370.1 ms;超时 100 ms 让健康下游的熔断器 20 秒里打开 16 次。
实测舱壁:共享 20 线程池里 D2 变慢后,没问题的 A 类请求 1 秒内成功 0.0%;信号量舱壁和独立线程池让 A 类保持 100.0%;Java 线程池四种拒绝策略(执行 8 / 20 / 8 / 8);nginx worker_connections 被慢上游占满时 /fast 成功只有 81%,给 /slow 加 limit_conn 6 后 100%。
五种限流算法的实测对照:窗口边界上固定窗口 1 秒放出 200 个(限额 100),滑动窗口日志 100 个;令牌桶放行突发、漏桶把突发摊平(中位等待 245 ms);滑动窗口日志每个 key 4320 字节对其余约 113 到 161 字节;Go 手写令牌桶并发下放行 320 个、上界 321。
实测限流的维度:全局一个桶时守规矩的用户 B/C/D 只放行 14%/19%/17%,每用户一个桶则 100%;共用桶时贵接口挤占便宜接口,按接口分桶后 /ping 100%;每个实例各限 100/s 时轮询流量放行 200 个/秒,等于没限。
实测 Redis 分布式令牌桶:读-改-写的不原子实现放行 3200 个(应为 1000),WATCH 要 3900 次冲突重试,Lua 脚本整体原子;客户端时间戳有 ±300 ms 偏差时放行 1500 个(应约 510);Redis 暂停时 fail-open 放行约 200/秒、fail-closed 几乎全拒、退回本地桶约等于限额。
实测自适应并发上限:300 个/秒打容量约 142/s 的服务,静态上限 256 时成功只有 2/s,静态 64 时 p99 823 ms,AIMD 和 gradient 142/s、p99 94 ms 和 201 ms;容量减半后静态 64 掉到 1/s,基准 rtt 取旧样本的 AIMD 卡在上限 1(10/s)。