首先来看一张官方的状态转换图:
zk状态图
首先zk客户端会创建一个连接请求,此时会创建一个session,用于标识此次与服务器进行连接的一次生命周期。session创建之后,客户端的连接状态变为connecting,当与服务器建立成功之后变为connected状态。

由上面的状态图可以看出客户端会接受到服务端返回的两个错误码,分别为CONNECTION_LOSSSESSION_EXPIRED,收到这两个错误码,客户端是怎么处理的呢?先看下CONNECTION_LOSS。

CONNECTION_LOSS

当客户端与服务端的连接发生异常时,会返回CONNECTION_LOSS。当客户端收到此返回码时,并不能盲目的认为是网络连接失败还是服务端挂了
因为返回CONNECTION_LOSS的情况可能有如下几种:

  • 客户端创建了一个请求,并且服务端已正常接收到了该请求,只是在返回时发生了异常(可能是网络异常)
  • 客户端创建了一个请求,服务端根本没有收到(可能是网络也可能是服务端挂了)

因此在客户端收到这样的返回码时,不能新建一个session。假如客户端与服务端A正常连接时,发生了异常,此时应该尝试与服务端B建立连接,如果此时新建session就会导致之前客户端与服务端A建立的一些watcher什么的就会消失,因为zk是以session来标识客户端与服务端连接的生命周期的。

当客户端收到这样的返回码之后,要做的事是根据服务器列表进行重连,如果在session过期之前,连接成功,则会话恢复,之前创建的watcher和临时节点什么的都依然存在。如果在session过期之后,则收到Session Expired事件。所以session超时时间应该大于连接超时时间。

SESSION_EXPIRED

当客户端收到SESSION_EXPIRED返回码时,会关闭与服务端的连接。

客户端与服务端第一次建立连接时,会生成一个session,在session超时之后客户端与服务端的连接才会跟换session。session是否过期是由服务端控制的。

当zk服务端超过一定的时间没有收到来自zk客户端的心跳,zk服务端就把这个session标记为过期,然后删除这个session创建的所有临时节点,并且马上通知所有监听了这些节点的其他session。此时,客户端由于处于断开连接的状态,并不知道当前session已经过期,只有在它与服务端重新建立连接之后,才会收到服务端发出的session过期通知。

这个流程是这样的:

  • 客户端1与服务端建立连接,此时session1被创建,连接正常;客户端2与服务端建立连接,此时session2被创建,连接正常(connected状态)
  • 客户端1与服务端连接异常,并尝试与服务器列表中的其它服务端建立连接(connecting状态)
  • 尝试与其它服务端建立连接的同时,时间也在一分一秒的流逝,超过了session1的超时时间
  • 服务端检测到session1已过超时时间,则通知session2,session1已超时。
  • 一段时间之后,可能是网络恢复也可能是客户端1程序恢复,已服务端建立的连接,但之前的session1已过期,收到了服务端的过期事件。(closed状态)
  • 重新创建session建立连接。

参考

https://wiki.apache.org/hadoop/ZooKeeper/FAQ#A2