1. 会话ID、连接断开、过期、超时概念
客户端和服务器端建立连接后,会生成 SessionId,客户端会定时向服务器发送心跳以重置下次 SessionTimeout 时间。生成的 SessionId 应该是存储在磁盘上的,服务器挂了重启还会尝试使用同一个 SessionId与客户机建立连接。若是客户端挂了,服务器使用 SessionId 会连不上客户端,此时 ZK 日志可能就会报错:WARN Unable to read additional data from client sessionid 0x10058c12dc2017a, likely client has closed socket(只是猜测未验证,但每次关闭ZK客户端和服务器,一重启就报错此错误,另外Session 超时可能也会报)
若连接断开,Zookeeper 会自己按如下流程自动尝试用同一个 sessionId 建立新连接:
捕获连接断开异常 ->获取一个新的ZK地址 -> 尝试连接,所以若发生 Connection Loss 情况,应用无需做什么,等待ZK客户端建立新连接即可。
若发生 Session Expired,通常是ZK客户端与服务器的连接断了,重新连上新的ZK服务器又很慢,超过了 Session Timeout 指定的时间,那么服务器认为这个 Session 已经过期了。由于在ZK中,很多数据和状态都是和会话绑定的,一旦会话失效,那么ZK就开始清除和这个会话有关的信息,包括这个会话创建的临时节点和注册的所有Watcher。网络恢复后,客户端可能会重新连接上服务器,但是很不幸,服务器会告诉客户端一个异常: SessionExpired。此时客户端的状态变成 CLOSED,应用需要自己编码做一些额外的处理,实现重新实例zookeeper对象,重新操作所有临时数据(包括临时节点和注册Watcher),总之,会话超时在ZK使用过程中是真实存在的。
客户端并不是可以随意设置会话超时时间,ZK服务器端有一个最小、最大的限制设置,由 minSessionTimeout 和 maxSessionTimeout 这两个参数设置。默认情况下 minSessionTimeout 值等于 2*tickTime 即两个滴答声, maxSessionTimeout 则是 20个滴答声,tickTime 默认值是 2000(即2秒),所以默认服务器端允许的超时时间在 4秒~40秒 之间,因此像 storm 这种客户端应用有个配置参数 storm.zookeeper.session.timeout,它的值就必须限制在其中,默认值是 20秒。更多 ZK 参数请看
官方文档。
2. 会话超时排查
工作中一台华为主备墙流量特别大,每分钟发8万条日志,平均每秒1300条,接过来后导致 supervisor 一会儿就挂了,测试环境模拟每秒发2000条持续10分钟却没重现客户问题,只能通过日志排查了。日志中存在大量 WARN,整理出来4类:
(1)(ConnectionStateManager-0] [WARN] Session timeout has elapsed while SUSPENDED. Injecting a session expiration. Elapsed ms: 20009. Adjusted session timeout ms: 20000) (2)Client session timed out, have not heard from server in 16105ms for sessionid 0x..., closing socket connection and attempting reconnect (3)[INFO] unable to reconnect to zookeeper service, session 0x... has expired, Closing socket connection. (4) [WARN] session expired event received
很明显这些都与 Zookeeper 会话有关,因此我将ZK服务器的 tickTime 增加到了 8000,即8秒,minSessionTimeout 和 maxSessionTimeout 则自动会增加到 16秒~160秒 之间。接着再去 storm.yaml 修改ZK客户端超时时间 storm.zookeeper.session.timeout: 80000(80秒,即10个滴答声),必须在16秒~160秒 之间。
测下来发现开头打印出的日志显示此配置是生效的,但后面的日志却又回到了 20000,有点奇怪。后来我是直接通过改ZK服务器,设置 minSessionTimeout 和 maxSessionTimeout 都为 80000,即忽视客户端任何设置,强制使用ZK服务器配置才搞定的。