ZooKeeper的每个客户端都维护一组服务端信息,在创建连接时由应用指定,客户端随机选择一个服务端进行连接,连接成功后,服务端为每个连接分配一个唯一标识。客户端在创建连接时可以指定溢出时间,客户端会周期性的向服务端发送PING请求来保持连接,当客户端检测到与服务端断开连接后,客户端将自动选择服务端列表中的另一个服务端进行重连。客户端允许应用修改服务端列表,但修改可能导致客户端与服务端的重连。
连接状态转换
ZooKeeper使用session来表示客户端和服务端的连接。ZooKeeper的客户端管理一个可用的服务端列表,ZooKeeper客户端首先创建一个handle,handle建立后处于CONNECTING状态,然后客户端随机选择一个服务端进行连接,连接成功后,handle的状态更换到CONNECTED状态。如果出现无法恢复的错误,例如;会话终止或者认证失败,或者应用直接关闭handle,handle将转换到CLOSED状态。
下面是一个ZooKeeper客户端状态转化图:
创建session
创建客户端session时,应用必须传入一组以逗号分隔的host:port列表,每个都对应一个ZooKeeper服务端,ZooKeeper客户端将选择任意一个服务端并尝试与其连接,如果连接失败,或者由于某些原因导致客户端与服务端连接断开,客户端将自动的选择列表中的另一个服务端进行连接,直到成功。当session创建成功后,ZooKeeper服务端为session分配一个唯一标识。
指定根路径
在ZooKeeper 3.2.0增加了可选的“chroot”后缀,可以改变当前客户端的根路径。例如,如果使用”127.0.0.1:4545/app/a”,客户端将使用”/app/a”作为其根路径,所有的路径都会相对于该路径。比如操作路径”/foo/bar”将真正对应到”/app/a/foo/bar”。这个特征在多租户环境下是非常有用的,可以简化客户端的应用逻辑。
session id
ZooKeeper的每个session都会被分配一个64-bit数字来标识,称为session id,被分配到客户端,如果客户端重连到一个不同的ZooKeeper服务端,该session id将被发送到该服务端。为了保障安全,服务端会为该session id创建一个所有ZooKeeper服务器都能验证的密码,当session被建立时,密码和session id一起被发送到客户端,客户端在与一个新的服务端重建连接时,客户端会将密码和session id一同发送到新的服务端。
溢出时间
ZooKeeper客户端在创建session时可以指定session的溢出时间,为一个毫秒值。客户端发送带溢出时间的请求,服务端的响应中将带上溢出时间。当前要求溢出时间的最小值为tickTime(在服务端配置)的2倍,最大值为tickTime的20倍。
当服务端未接收到客户端的消息的时间达到溢出时间后,该session将被终止,详见“重连”。
重连
当客户端从ZK服务端簇中分离时,它将开始从创建session时指定的服务端列表中选择下一个服务器重建连接,session要么重新转化到”connected”状态(连接处在溢出时间内),要么转换到”expired”状态(连接时间溢出)。
上诉所有操作都应该交由Zk客户端自动处理,只有当连接时间溢出后,才应该创建一个新的session。
session溢出由Zk服务端管理,如果客户端建立session时指定了溢出时间,并且ZK服务端在该时间周期内没有接收到来自客户端的任何消息(例如心跳),session将被断开。session断开时,ZK服务端将删除所有该session的临时节点,并立刻通知其他监听变化的客户端。当TCP连接重建连接后,溢出session的客户端将接收到”session expired”通知。
下面是对整个过程的详细描述:
- session成功创建,session进入’connected’状态;
- 客户端与服务端分离;
- 客户端失去与服务端的连接;
- 当时间溢出,服务端簇终止session,但客户端不会受到任何通知;
- 客户端与服务端重新建立TCP连接;
- 客户端重连到服务端,并接收到”session expired”通知。
session通过客户端的请求来保持活跃,如果session的空闲时间达到溢出时间,客户端将发送一个PING请求以保持session活跃。PING请求既可以通知服务端客户端是活跃的,也能检测客户端与服务端的连接可用。考虑到网络延迟和服务端处理能力等因素,PING请求的时间点应该足以保证足够检测到一个失效的连接并重连到一个新的服务端,换句话说,就是PING的时间点应该保证检测到连接失效后,客户端重新连接到服务端的时间不会导致session溢出。
异常处理
当客户端与服务端的连接建立后(connected),以下操作会导致连接异常:
1. 应用在一个不再活跃/有效的session上执行操作;
2. 当存在客户端与服务端的异步操作正在执行时,客户端执行断开连接操作。
SessionMovedException
版本3.2.0增加。
SessionMovedException是一个内部异常,不会暴露到客户端。当服务端收到一个请求,该请求的连接对应的session已经被移动到其他服务端,则抛出该异常。
这个异常出现的原因通常是:客户端发送了一个请求到服务端,但由于网络延迟,以至于客户端时间溢出并连接到了一个新的服务端,当旧的请求到达服务端时,服务端探测到session已经被移到新的服务端,将抛出异常并关闭连接。
客户端通常不从旧的连接读取信息,因此不会关注到该异常。但存在一种场景该异常将被看到:两个客户端尝试重建同一个连接(使用相同的session id和密码),客户端中的一个将重建连接成功,而另一个将失败(如果不关注该异常,将导致失败者无限期的尝试重建链接)。
更新服务器列表
客户端在建立session时可以指定服务端列表(host:port列表),ZooKeeper允许客户端更新该列表。该操作可能会导致客户端重连到一个新的服务端:
1. 如果当前的主机不在新列表中,将必然导致重新连接;
2. 如果服务端列表的数量有增加或减少,根据负载均衡算法,可能会导致重新连接。
例如,如果先前的列表中包含了3个主机,而现在的列表中包含了原来的3个和新增的2个,原来的客户端将有40%会重连到新增的2个主机中的一个上以平衡负载。
再假如先前有5台主机,新的列表减少了2台主机,连接到剩下3台主机的客户端将保持连接,而所有连接到移除主机的客户端将会移动到剩下的3台主机上,主机的选择采用一种随机算法。