会话的作用:

zk客户端和服务器在建立socket长连接的时候,服务器端创了一个session ,用来存储这次会话用到的一些消息。

因为网络原因或者服务器原因,socket 断开,客户端尝试重新连接,等到重新连接上,发现之前的session已经不在了这时候服务器会告诉客户端会话超时,客户端会关闭session,然后开启新的session.。

服务器会定时判断session 是否超时,如果超时会清除这个session, 存储的信息随之消失

 

会话生命周期:

在ZooKeeper中,客户端和服务端建立连接后,会话随之建立,生成一个全局唯一的会话ID(Session ID)。服务器和客户端之间维持的是一个长连接,在SESSION_TIMEOUT时间内,服务器会确定客户端是否正常连接(客户端会定时向服务器发送heart_beat,服务器重置下次SESSION_TIMEOUT时间)。因此,在正常情况下,Session一直有效,并且ZK集群所有机器上都保存这个Session信息。

异常情况1 : CONNECTIONLOSS(连接断开)

网络的闪断或是客户端所连接的服务器挂机,客户端与当前连接的那台服务器之间连接断了,

ZK客户端首先会捕获异常---》然后获取一个新的ZK地址 ——> 尝试连接
在这个流程中,我们可以发现,整个过程不需要开发者额外的程序介入,都是ZK客户端自己会进行的,并且,使用的会话ID都是同一个,所以结论就是:发生CONNECTIONLOSS的情况,应用不需要做什么事情,等待ZK客户端建立新的连接即可

异常情况2 : SESSIONEXPIRED(Session过期)

这个通常是ZK客户端与服务器的连接断了,试图连接上新的ZK机器,但是这个过程如果耗时过长,超过了SESSION_TIMEOUT 后还没有成功连接上服务器,那么服务器认为这个Session已经结束了(服务器无法确认是因为其它异常原因还是客户端主动结束会话),由于在ZK中,很多数据和状态都是和会话绑定的,一旦会话失效,那么ZK就开始清除和这个会话有关的信息,包括这个会话创建的临时节点和注册的所有Watcher。在这之后,由于网络恢复后,客户端可能会重新连接上服务器,但是很不幸,服务器会告诉客户端一个异常:SESSIONEXPIRED(会话过期)。此时客户端的状态变成 CLOSED状态,应用要做的事情就是的看自己应用的复杂程序了,要重新实例zookeeper对象,然后重新操作所有临时数据(包括临时节点和注册Watcher),总之,会话超时在ZK使用过程中是真实存在的。

 

客户端将连接哪一个服务器?

在Quorum模式,一个客户端拥有多个服务器可以连接。然而在Standalone模式,它必须尝试重新有效地连接到那个唯一的服务器。在Quorum模式,应该会传一个服务器列表到客户端,客户端从中选择一个连接。

当尝试连接另一个服务器时,很重要的一点是这个服务器的ZooKeeper状态至少要和客户端已经观察到的最近ZooKeeper状态是一样新的。客户端不能连接到一个这样的服务器。它没有看到客户端可能已经看到的更新。Zookeeper通过在服务中排序更新操作来决定新鲜程度(Freshness)。每一个对Zookeeper布局状态的改动操作相对于所有其它执行的更新操作都是全序的,所以如果一个客户端已经在位置i观察到一个更新,它不能连接一个仅看到i' < i的服务器。在ZooKeeper的实现中,系统分配给每个更新操作一个事务ID来建立这个顺序。

事务ID(zxids)在重连中的使用:客户端因为超时和s1断开连接后,它尝试连接s2,但是s2已经落后了,并不能反应客户端已知的更新。而s3已经看到和客户端一样看到的更新,所以它被安全连接。

 

客户端什么时候向服务器发送心跳消息?

在创建Session时,需要设置Session Timeout这个重要参数。这是Zookeeper服务允许一个Session在定义它失效之前的时间。如果服务在时间t内不能看到与一个Session关联的消息,它将定义这个Session失效。如果客户端在1/3 t时间内没有听到任何从服务器过来的消息,它将发送一个心跳消息给服务器。在(2/3)t时间, Zookeeper客户端开始寻找另一个Zookeeper服务器,并且它有另外的(1/3)t的时间寻找。

 

会话超时时间

在《ZooKeeper API 使用》一文中已经提到,在实例化一个ZK客户端的时候,需要设置一个会话的超时时间。这里需要注意的一点是,客户端并不是可以随意设置这个会话超时时间,在ZK服务器端对会话超时时间是有限制的,主要是minSessionTimeout和maxSessionTimeout这两个参数设置的。(详细查看这个文章《ZooKeeper管理员指南》)Session超时时间限制,如果客户端设置的超时时间不在这个范围,那么会被强制设置为最大或最小时间。 默认的Session超时时间是在2 * tickTime ~ 20 * tickTime。所以,如果应用对于这个会话超时时间有特殊的需求的话,一定要和ZK管理员沟通好,确认好服务端是否设置了对会话时间的限制

很多同学可能有这样的疑问,我明明把连接zk客户端的超时时间sessionTimeout设置为180秒了,可是为什么仅仅过了40几秒就超时了?

    其实只这么设置,根本没有任何作用,因为客户端将sessionTimeout的值传给zk时,zk还会根据minSessionTimeout与maxSessionTimeout两个参数重新调整最后的超时值

Java代码  

public int getMinSessionTimeout() {  

  return minSessionTimeout == -1 ? tickTime * 2 : minSessionTimeout;  

}  

public int getMaxSessionTimeout() {  

 return maxSessionTimeout == -1 ? tickTime * 20 : maxSessionTimeout;  

}  

就是说这两个值,默认分别为tickTime 的2倍和20倍,而tickTime的默认值是3秒,即最后生效的超时时间,一定是6s<timeout<60s,原因如下:

Java代码  

  1. int minSessionTimeout = zk.getMinSessionTimeout();  
  2. if (sessionTimeout < minSessionTimeout) {  
  3.     sessionTimeout = minSessionTimeout;  
  4. }  
  5. int maxSessionTimeout = zk.getMaxSessionTimeout();  
  6. if (sessionTimeout > maxSessionTimeout) {  
  7.     sessionTimeout = maxSessionTimeout;  
  8. }  

 

Logo

权威|前沿|技术|干货|国内首个API全生命周期开发者社区

更多推荐