现在的位置: 首页 > 综合 > 正文

Curator源码解析(四)ZooKeeper存在的连接问题

2018年04月08日 ⁄ 综合 ⁄ 共 2441字 ⁄ 字号 评论关闭

都说Curator的连接机制比较牛逼,所以在分析Curator的连接和重试机制之前,我想先搞清楚原生的ZooKeeper的连接存在哪些问题。

下面是我查阅资料总结的结果,转载请注明出处: jiq•钦's technical Blog

Curator虽然提供所谓的高层抽象API来简化了ZooKeeper的使用,但更重要的是封装了管理到ZooKeeper集群的连接以及重试机制的复杂性,下面我们来详细分析一下Curator在这方面都是怎么做的,不过在这之前先要搞清楚ZooKeeper目前在连接方面有哪些问题。



会话建立

当你new出一个ZooKeeper对象的时候,客户端就建立了与ZooKeeper服务端之间的一个Session(注意这个session是线程安全的,即多个线程可以共用一个ZooKeeper实例),这个Session会有一个超时时限,即失效时间,通过ZooKeeper的构造函数传递进去,客户端会不断发送心跳(多久发送一次和设置的会话超时时限有关)到ZooKeeper服务端,以保持与ZooKeeper服务端的有效连接和Session的有效性。

 

连接丢失

如果客户端与服务端的网络断开,或客户端连接的ZooKeeper server挂掉,或者与Server的Session的建立后连接还未建立完成,都会出现CONNECTION_LOSS现象,客户端所有watcher都会收到一个disconnected event,客户端连接状态从CONNECTED变为CONNECTING。

 

自动重连

此时客户端库会自动从ZooKeeper服务器列表中选择一个server来进行重连。

(备注:创建zk实例的时候就会新建两个后台线程,一个用于维护watcher,一个用户数据传输)

A.  如果重新成功建立与服务端的TCP连接,并且没有超过session的超时时限,那么ZooKeeper客户端将会收到一个SyncConnected event,客户端连接状态就又会变为CONNECTED,连接恢复正常且临时节点和注册的watch事件也不会被删除掉。即使重连在一个很短的时间内完成,也会收到了两个事件。

B.  如果过了很久还是不能重新成功建立与服务端的TCP连接,客户端将会一直保持在disconnected状态,也就永远不会收到Expired event,只会有disconnectedevent(因为事件是来自服务端)。

C.  如果重新成功建立与服务端的TCP连接,但是发现已经超过了session设置的超时时限,那么客户端将会收到一个Expired event,表示会话已经终止SESSION_EXPIRED,此时服务器会将这个客户端注册的所有watcher,以及创建的临时节点全部删除,同时客户端持有的ZooKeeper句柄也会被关闭,唯一能做的就是重建ZooKeeper对象。发生SESSION_EXPIRED的watcher将会看到如下状态转换:

'connected': 会话建立,客户端与ZooKeeper集群正常通信

....client is partitioned from the cluster

'disconnected': 客户端丢失与ZooKeeper集群的连接

....time elapses, 时间逐渐流逝,在'timeout'时限后ZooKeeper集群将会终止这个会话,此时处于disconnected状态的客户端将会什么都看不到。

....time elapses, 时间流逝,客户端重新建立与ZooKeeper集群的连接

'expired': 最终客户端重连到ZooKeeper集群,将会收到expiration 通知

特别注意: 关于ZooKeeper会话终止我这里有一篇文章详细讲解!!!

 

连接丢失的处理:

当CONNECTION_LOSS发生的时候,任何正在运行的ZooKeeper操作(like getChildren,get/setData, create, delete)都将会抛出ConnectionLossexception,你必须处理这个异常,一种处理方式就是重试这些操作直到成功为止,即直到ZooKeeper连接自动重新建立回来。

但是当一个操作抛出ConnectionLossexception,你无法确定是否这个操作是不是已经成功了,CONNECTION_LOSS意味着客户端和服务器端的TCP连接断开,但是并不意味着请求失败。假如正在执行一个create请求,然后在请求到达服务器以及response返回之前,连接断开,这个create请求就会执行成功,假如在数据包发送到线路之前断开那么create请求就会执行失败。很不幸客户端没办法知道在CONNECTION_LOSS发生后自己的请求是否执行成功。那么怎么办呢?

个人认为,这个问题在于需要明白什么时候需要重试,比如发生连接丢失和操作超时这类异常时是肯定需要重试的,至于其他异常(create操作可能引起NodeExists 异常, delete操作可能引起NoNode异常)我们无需关心,无需捕获这些异常进行重试,只需要简单将它们抛出即可。

 

会话终止的处理:

SESSION_EXPIRED将会自动关闭ZooKeeper句柄,如果正确操作ZooKeeper集群,会话终止现象很难出现,如果客户端强制关闭一个连接倒是一定会出现这个事件,因为服务器认为客户端已经死掉了。

如果真的出现会话失效该如何处理呢?

首先当发生SESSION_EXPIRED时,客户端持有的ZooKeeper句柄会被关闭,如果还想应用程序继续执行,就必须要重建ZooKeeper对象。

其次当发生SESSION_EXPIRED时,服务端会把客户端注册的所有watcher,以及创建的临时节点全部删除,如果这些东西对应用程序来说至关重要,那么你唯一能做的就是根据实际情况自己重新创建这些临时节点以及注册对应的watcher。

 

之后文章将分析一下看Curator是如何处理这两个问题的。


抱歉!评论已关闭.