熔断器用计数窗口还是时间窗口?失败率阈值和慢调用率阈值怎么选?这篇用虚拟时钟模拟和本机的 HTTP 服务,把触发时间、误触发和慢调用的盲区量出来。
请求速率 | 计数窗口:发现延迟 | 已经打到坏下游的请求 | 时间窗口:发现延迟 | 已经打到坏下游的请求 --------+-----------+------------+-------------------+----------- 0.5 次/秒 | 18.00 s | 10 | 不触发(窗口里不到 10 次调用) | 1500 1 次/秒 | 9.00 s | 10 | 4.00 s | 5 2 次/秒 | 4.50 s | 10 | 4.50 s | 10 5 次/秒 | 2.00 s | 10 | 5.20 s | 26 20 次/秒 | 0.45 s | 10 | 4.95 s | 100 100 次/秒 | 0.09 s | 10 | 4.99 s | 500 500 次/秒 | 0.02 s | 10 | 5.00 s | 2501 计数窗口的发现时间 ≈ 10 次 ÷ 速率,速率越高越快,速率低时要等很久;时间窗口只要窗口里调用数够就是看比例,发现时间在 5 s 上下(窗口一半的时间),与速率无关,但速率低到窗口里不够最少调用数时永远不会触发
计数窗口按“多少次失败”触发,速率越高越快;时间窗口按比例触发,约窗口一半的时间,低速率时凑不够最少调用数就永不触发。
配置 | 只有 2% 基线失败时的误触发次数 | 加上 5 秒小故障时的误触发次数 | 真故障(100% 失败)的发现延迟 ------------------------------+-------------------+------------------+------------------ 计数 20 次 / 最少 20 / 失败率 ≥ 50% | 0 | 43 | 0.18 s 计数 20 次 / 最少 20 / 失败率 ≥ 20% | 104 | 328 | 0.06 s 计数 100 次 / 最少 100 / 失败率 ≥ 20% | 0 | 50 | 0.38 s 时间 10 s / 最少 20 / 失败率 ≥ 50% | 0 | 0 | 4.98 s 时间 10 s / 最少 20 / 失败率 ≥ 20% | 0 | 0 | 1.98 s 时间 60 s / 最少 20 / 失败率 ≥ 20% | 0 | 0 | 4.98 s 6 小时里一共有 18 次 5 秒的小故障;小窗口里随机失败的个数波动很大(20 次调用、失败率 30% 时,一个窗口里失败 ≥ 10 次的概率约 4.8%,一个 5 秒小故障有 250 个这样的窗口),所以计数窗口小的配置会被短暂的小故障误触发;时间窗口按比例看,6 小时里没有误触发
小窗口里失败个数波动大,短暂小故障就能触发;时间窗口按比例看更稳,代价是发现真故障慢几秒。
熔断器 | 打开的时刻 | 服务变慢之后(1 s 起)发出的调用 | 这些调用占用线程的总时间 -------------+--------+--------------------+------------- A 只看失败率 | 没有触发 | 120 | 241 s B 失败率 + 慢调用率 | 3.01 s | 26 | 52 s (连接类错误 0 次;不是超时,应为 0) 慢调用要等调用返回才能被记成“慢”,所以打开的时刻不会早于第一批慢调用完成(服务变慢在 1 s,每个要 2 s,最早约 3 s);A 永远看不见这种“慢但不错”的下游,20 个线程一直被拖在里面
只看失败率看不见只慢不错的下游;加上慢调用率可以打开,但要等慢调用返回之后。