bigywater.tech
ZooKeeper · 97

Curator RetryPolicy 实测

Curator 的重试策略只有一行配置,但它决定了服务端抖一下时你的调用是失败还是成功。这篇用真实的不可用服务端和真实的重启,量 6 种策略放弃前花多久、策略对象在内部被怎么调用,以及哪种策略能撑过一次重启。

2.3–7.9 s
服务端不可用时 6 种策略放弃前的耗时范围
2 条线程
同一个策略对象被 main 与 Curator-Framework 两条线程各用一遍
1 次不够
RetryNTimes(1, 100) 撑不过 3 秒左右的重启,其余 3 种撑过了

不可用时的放弃时间

实测 · 本机执行输出
  RetryOneTime(500)                              → ConnectionLossException,共 2.3 s;各线程里 allowRetry 的睡眠序列 {Curator-Framework=[500 ms, 放弃], main=[500 ms, 放弃]}
  RetryNTimes(3, 200)                            → ConnectionLossException,共 4.6 s;各线程里 allowRetry 的睡眠序列 {Curator-Framework=[200 ms, 200 ms, 200 ms, 放弃], main=[200 ms, 200 ms, 200 ms, 放弃]}
  ExponentialBackoffRetry(100, 5)                → ConnectionLossException,共 6.8 s;各线程里 allowRetry 的睡眠序列 {Curator-Framework=[100 ms, 100 ms, 500 ms, 800 ms, 600 ms, 放弃], main=[100 ms, 100 ms, 500 ms, 400 ms, 900 ms, 放弃]}
  ExponentialBackoffRetry(100, 5, maxSleep=300)  → ConnectionLossException,共 6.8 s;各线程里 allowRetry 的睡眠序列 {Curator-Framework=[100 ms, 200 ms, 300 ms, 300 ms, 300 ms, 放弃], main=[100 ms, 300 ms, 300 ms, 300 ms, 300 ms, 放弃]}
  BoundedExponentialBackoffRetry(100, 800, 6)    → ConnectionLossException,共 7.9 s;各线程里 allowRetry 的睡眠序列 {Curator-Framework=[100 ms, 100 ms, 400 ms, 500 ms, 800 ms, 800 ms, 放弃], main=[100 ms, 100 ms, 200 ms, 100 ms, 800 ms, 800 ms, 放弃]}
  RetryUntilElapsed(2000, 300)                   → ConnectionLossException,共 3.8 s;各线程里 allowRetry 的睡眠序列 {Curator-Framework=[300 ms, 300 ms, 300 ms, 300 ms, 300 ms, 放弃], main=[300 ms, 300 ms, 300 ms, 放弃]}

连接超时设为 500 ms,所以每次尝试连接本身就要 0.5 s;总耗时是「重试间隔 + 每次尝试连接的等待」的和,不是只看重试间隔。

策略被调用两遍

实测 · 本机执行输出
  RetryOneTime(500)                              → ConnectionLossException,共 2.3 s;各线程里 allowRetry 的睡眠序列 {Curator-Framework=[500 ms, 放弃], main=[500 ms, 放弃]}
  RetryNTimes(3, 200)                            → ConnectionLossException,共 4.6 s;各线程里 allowRetry 的睡眠序列 {Curator-Framework=[200 ms, 200 ms, 200 ms, 放弃], main=[200 ms, 200 ms, 200 ms, 放弃]}
  ExponentialBackoffRetry(100, 5)                → ConnectionLossException,共 6.8 s;各线程里 allowRetry 的睡眠序列 {Curator-Framework=[100 ms, 100 ms, 500 ms, 800 ms, 600 ms, 放弃], main=[100 ms, 100 ms, 500 ms, 400 ms, 900 ms, 放弃]}
  ExponentialBackoffRetry(100, 5, maxSleep=300)  → ConnectionLossException,共 6.8 s;各线程里 allowRetry 的睡眠序列 {Curator-Framework=[100 ms, 200 ms, 300 ms, 300 ms, 300 ms, 放弃], main=[100 ms, 300 ms, 300 ms, 300 ms, 300 ms, 放弃]}
  BoundedExponentialBackoffRetry(100, 800, 6)    → ConnectionLossException,共 7.9 s;各线程里 allowRetry 的睡眠序列 {Curator-Framework=[100 ms, 100 ms, 400 ms, 500 ms, 800 ms, 800 ms, 放弃], main=[100 ms, 100 ms, 200 ms, 100 ms, 800 ms, 800 ms, 放弃]}
  RetryUntilElapsed(2000, 300)                   → ConnectionLossException,共 3.8 s;各线程里 allowRetry 的睡眠序列 {Curator-Framework=[300 ms, 300 ms, 300 ms, 300 ms, 300 ms, 放弃], main=[300 ms, 300 ms, 300 ms, 放弃]}

我没有去读 Curator 源码查这两条线程具体对应什么;只确认了日志里这两条线程各自从第 0 次开始计数。所以不要只按「策略里写的次数 × 间隔」去估算一次失败调用的总耗时,要在自己的环境里测。

能否撑过一次重启

实测 · 本机执行输出
  RetryNTimes(1, 100)                → ConnectionLossException,发起后 1.3 s 返回
  RetryNTimes(5, 200)                → 成功,发起后 3.5 s 返回
  ExponentialBackoffRetry(100, 5)    → 成功,发起后 2.8 s 返回
  RetryUntilElapsed(10000, 200)      → 成功,发起后 3.3 s 返回

服务端重启期间的客户端行为,也可以对照kazoo的「调用一直等到恢复」。Curator 里则是由策略决定等多久。

分步演示

重试策略
本步的对象

    本文用到的工具与边界

    • 本机 macOS 27 / ZooKeeper 3.9.6(brew 安装的 jar,用 java -cp 直接启动自己的实例,OpenJDK 17 Temurin,JVM 堆 128 MiB)/ 本机回环端口 7300+,数据在临时目录;默认 tickTime=2000;计时用 time.perf_counter,数字每次运行会有小幅抖动。Curator 5.7.1 + OpenJDK 17;连接超时 500 ms、会话超时 6 s;第二项的重启是 SIGTERM 停止后 1.5 s 再启动(含启动时间实际不可用约 2.5–3 s),每个策略只测了 1 次
    • 没有验证:同一策略重复多次的耗时方差(指数退避有随机性)、ConnectionStateErrorPolicy、两条线程各自对应的框架内部循环、RetryForever / SleepingRetry 的其它实现
    京ICP备2026067502号-1京公网安备11010502063487号