bigywater.tech
ZooKeeper · 13

Session 超时与重连

会话什么时候算过期?过期之后客户端看到什么?这篇用「暂停服务端」「冻结客户端」两种方式,把会话超时前后的状态变化按毫秒记下来,这也是理解 GC 停顿、网络抖动为什么会让锁「丢掉」的基础。

短停顿无感知
服务端暂停 2 s(< 会话超时 6 s):客户端没有任何状态变化
LOST → 新会话
过期后客户端被告知 LOST 并建立新会话,id 变了,临时节点没了
6.2 s
客户端被冻结后,服务端删除其临时节点的时间(会话超时 6 s)

短暂的停顿

实测 · 本机执行输出
  状态变化: []
  同一个会话: True | 临时节点还在: True

超过会话超时

实测 · 本机执行输出
  状态变化: [(3479, 'SUSPENDED'), (9007, 'LOST'), (9140, 'CONNECTED')]
  原会话 id 0x1000bc872a30002 → 现在 0x1000bc872a30003;临时节点 /eph2 还在: False

这里服务端本身被暂停,所以「会话过期」是服务端恢复之后才判定并通知的;如果是真正的网络分区(服务端一直在运行),服务端会在超时到期时就删除临时节点,客户端要等到重新连上才知道。

客户端被冻结

实测 · 本机执行输出
  客户端进程冻结前 /eph3 存在: True
  冻结客户端后 /eph3 在 6208 ms 后被服务端删除(会话超时 6000 ms)

这就是 GC 长停顿会让分布式锁失效的原因:客户端停顿超过会话超时,服务端已经删掉了它的锁节点,它恢复后还以为自己持有锁。解决办法是客户端在 SUSPENDED / LOST 时停止依赖锁的操作(第 79 篇),本文只验证了现象。

分步演示

会话过期
本步的对象

    本文用到的工具与边界

    • 本机 macOS 27 / ZooKeeper 3.9.6(brew 安装的 jar,用 java -cp 直接启动自己的实例,OpenJDK 17 Temurin,JVM 堆 128 MiB)/ 本机回环端口 7300+,数据在临时目录;默认 tickTime=2000;计时用 time.perf_counter,数字每次运行会有小幅抖动。单机,tickTime=1000,客户端请求会话超时 6 s;暂停 / 冻结用 SIGSTOP / SIGCONT;状态来自 kazoo 的状态监听器
    • 没有验证:真实网络分区、集群里 leader 切换时的会话迁移(第 36 篇)、kazoo 自动重试的行为、客户端在 SUSPENDED 期间发出的请求如何处理
    京ICP备2026067502号-1京公网安备11010502063487号