4.4 Session 管理:出庭资格的存续与吊销


文档摘要

4.4 Session 管理:出庭资格的存续与吊销 本节摘要:会话是客户端与服务端之间的有租约契约:心跳续租、超时吊销、吊销即清算临时节点与监听。本节讲清会话的登记与分桶续租机制、断线与过期的语义分野、以及临时节点清算引发的连锁反应——锁与注册中心的"自动回收"全部根植于此。 一张有租约的出庭资格证 每次客户端成功连接,服务端签发一张资格证:会话 id 加租约时长。租约靠心跳续期——客户端在超时三分之一处发 PING,收到应答即续租。只要续租不断,资格有效;连续一个超时周期无心跳,资格吊销。这套"租约"设计的深意在于:服务端不需要网络层面的判断来认定客户端死活,只认心跳。进程被 kill、机器断电、网络对端黑洞,在服务端眼里都是同一件事——心跳停了,租约到期,资格吊销。

4.4 Session 管理:出庭资格的存续与吊销

本节摘要:会话是客户端与服务端之间的有租约契约:心跳续租、超时吊销、吊销即清算临时节点与监听。本节讲清会话的登记与分桶续租机制、断线与过期的语义分野、以及临时节点清算引发的连锁反应——锁与注册中心的"自动回收"全部根植于此。

一张有租约的出庭资格证

每次客户端成功连接,服务端签发一张资格证:会话 id 加租约时长。租约靠心跳续期——客户端在超时三分之一处发 PING,收到应答即续租。只要续租不断,资格有效;连续一个超时周期无心跳,资格吊销。这套"租约"设计的深意在于:服务端不需要网络层面的判断来认定客户端死活,只认心跳。进程被 kill、机器断电、网络对端黑洞,在服务端眼里都是同一件事——心跳停了,租约到期,资格吊销。

两个词的分野:disconnected 与 expired

客户端侧最难分清的就是这两个状态。disconnected 是网络断开:TCP 断了或换了一台服务器连,但租约还没到期,临时节点还挂着,重连成功后一切照旧。expired 是租约作废:服务端已按超时清算,客户端即便随后重连成功,拿到的也是"会话已过期"的正式通知,本地必须全量重建——重挂所有监听、重建临时节点、重走选主。

// 用连接状态监听把三态处理写全:生产代码的最低配置 client.getConnectionStateListenable().addListener((c, newState) -> { switch (newState) { case CONNECTED: System.out.println("会话可用"); // 首次连接或重连成功 break; case SUSPENDED: System.out.println("连接断开,租约仍在计时"); // 可继续使用内存缓存,但别发起新的写——可能失败 break; case LOST: System.out.println("会话已过期,全量重建开始"); rebuildAllWatches(); // 重挂监听 recreateEphemerals(); // 重建临时节点,触发重新选主 break; default: break; } });

这段代码的 LOST 分支是第五章锁判例正确性的根基:会话过期意味着临时节点已被清算,你持有的锁随之自动释放——不是"可能释放",是确定释放。反过来,把 LOST 误当 SUSPENDED 处理(不重建),就是 3.1 节那桩"锁被两人持有"事故的完整病根。

图:会话状态机与吊销清算链

图:会话状态机与吊销清算链

分桶续租:服务端的节拍管理

服务端不逐个会话扫表,而是把会话按下一次到期时刻分进一串时间桶(默认按 expiryInterval,约 tickTime 级别),到期桶整体处理。心跳到达时把会话从旧桶挪进新桶——这就是"续租"的服务端实现。这个设计的可观察推论:实际吊销时刻不是精确的超时点,而是所在桶的清空时刻,误差在一个桶宽以内。给锁定的临界区留余量时,要按"超时值加一个桶宽"估。

# 服务端视角验证:四字命令 dump 列出全部会话与临时节点归属 $ echo dump | nc node1 2181 Sessions with Ephemerals (0): 0x1000a3f2c1 /services/pay/inst-0000000001 # 会话 0x...c1 名下的临时节点 0x1000a3f7d2 /locks/pay-0000000004 # 锁节点:会话死则节点亡,锁自动过户 # cons 命令看会话活性:queued 与 recved 的增长即心跳在续租 $ echo cons | nc node1 2181 /127.0.0.1:52345[1](queued=0,recved=812,sent=812,sid=0x1000a3f2c1,...)

dump 的输出是排查"僵尸持有者"的第一现场:锁节点还在,但归属会话已经不在 Sessions 列表里——说明客户端进程活着、网络正常,却因为 GC 长停顿或线程阻塞导致心跳断供,租约已被吊销。第六章 6.3 节的会话抖动案将完整解剖这类现场。

调参实战:租约多长才合适

会话超时是 2.3 节协商边界的客户端端。取值是一笔三方交易:太短,一次 GC 停顿或网络抖动就吊销资格,临时节点无谓清算,选主反复抖动;太长,真死的客户端迟迟不退场,锁与注册位空等。经验区间是 30 到 60 秒起——至少覆盖你应用的最大 GC 停顿,锁场景宁可再放宽。还记得 4.1 节吗?心跳本身也是要写日志与会话的事务,超长会话与超短会话都不是免费的。

// 客户端设置:Curator 里 sessionTimeoutMs 的取值要与 GC 情况对表 CuratorFramework client = CuratorFrameworkFactory.builder() .connectString("node1:2181,node2:2181,node3:2181") .sessionTimeoutMs(60000) // 60 秒:覆盖常见的 10 至 30 秒 Full GC 并留余量 .retryPolicy(new ExponentialBackoffRetry(1000, 3)) .build();

要点回顾

  • 会话即租约:心跳续租、超时吊销,服务端只认心跳不猜死活;吊销时刻有桶宽级误差。
  • disconnected 与 expired 之间隔着清算:前者重连即愈,后者必须全量重建,处理错位就是锁事故。
  • 清算三件事:删临时节点、发监听事件、注销监听表——注册中心、锁、选主的自动行为全是下游。
  • 超时与 GC 对表:sessionTimeout 至少覆盖最大停顿,锁场景更宽;过短抖动、过长拖场。

常见疑问

问:会话过期后重连,会话 id 会变吗? 会。旧会话已注销,重连建立的是全新会话。任何把"重连"与"会话恢复"划等号的代码,在临时节点场景下都是错的。

本节最后一个案件回到案卷本身:谁有权读、谁有权写——ACL 登记簿。

延伸追问

问:客户端空闲时心跳还发吗? 发,心跳与会话活性绑定而与业务读写无关——SDK 的网络线程自动续租。这意味着长空闲的连接也在消耗服务端会话资源,连接池里闲置的客户端应当关闭而不是留着;反过来说,"连接数"监控(6.4 节)要区分活性连接与僵尸连接。


作者与出处
原作者: 灏天文库
来源:灏天文库
整理: 灏天文库整理
由灏天文库平台收录,内容或由平台用户上传,仅供学习交流
发布者: 作者: 灏天文库 转发
评论区 (0)
U