ZooKeeper架构
现在我们已经讨论了ZooKeeper暴露给应⽤的⾼层操作,我们需要详细了解服务实际上是如何运⾏的。应⽤通过客户端库来对ZooKeeper实现了调⽤。客户端库负责与ZooKeeper服务器端进⾏交互。
ZooKeeper仲裁
在仲裁模式下,ZooKeeper复制集群中的所有服务器的数据树。但如果让⼀个客户端等待每个服务器完成数据保存后再继续,延迟问题将⽆法接受。在公共管理领域,法定⼈数是指进⾏⼀项投票所需的⽴法者的最⼩数量。⽽在ZooKeeper中,则是指为了使ZooKeeper⼯作必须有效运⾏的服务器的最⼩数量。这个数字也是服务器告知客户端安全保存数据前,需要保存客户端数据的服务器的最⼩个数。例如,我们⼀共有5个ZooKeeper服务器,但法定⼈数为3个,这样,只要任何3个服务器保存了数据,客户端就可以继续,⽽其他两个服务器最终也将捕获到数据,并保存数据。
例子
为了明⽩这到底是什么意思,让我们先来通过⼀个例⼦来看看,如果法定⼈数太⼩,会如何出错。假设有5个服务器并设置法定⼈数为2,现在服务器s1和s2确认它们需要对⼀个请求创建的znode/z进⾏复制,服务返回客户端,指出znode创建完成。现在假设在复制新的znode到其他服务器之前,服务器s1和s2与其他服务器和客户端发⽣了长时间的分区隔离,整个服务的状态仍然正常,因为基于我们的假设设定法定⼈数为2,⽽现在还有3个服务器,但这3个服务器将⽆法发现新的znode/z。因此,对创建节点/z的请求是⾮持久化的。
会话
在对ZooKeeper集合执⾏任何请求前,⼀个客户端必须先与服务建⽴会话。会话的概念⾮常重要,对ZooKeeper的运⾏也⾮常关键。客户端提交给ZooKeeper的所有操作均关联在⼀个会话上。当⼀个会话因某种原因⽽中⽌时,在这个会话期间创建的临时节点将会消失。
通常,⼀个客户端只打开⼀个会话,因此客户端请求将全部以FIFO顺序执⾏。
如果客户端拥有多个并发的会话,FIFO顺序在多个会话之间未必能够保持。⽽即使⼀个客户端中连贯的会话并不重叠,也未必能够保证FIFO顺序。
并发问题
下⾯的情况说明如何发⽣这种问题:
- 客户端建立了⼀个会话,并通过两个连续的异步调用来创建/tasks和/workers。
会话的状态和声明周期
会话的⽣命周期(lifetime)是指会话从创建到结束的时期,⽆论会话正常关闭还是因超时⽽导致过期。为了讨论在会话中发⽣了什么,我们需要考虑会话可能的状态,以及可能导致会话状态改变的事件。
⼀个会话从NOT_CONNECTED状态开始,当ZooKeeper客户端初始化后转换到CONNECTING状态(图2-6中的箭头1)。正常情况下,成功与ZooKeeper服务器建⽴连接后,会话转换到CONNECTED状态(箭头2)。
注意:发⽣⽹络分区时等待CONNECTING
如果⼀个客户端与服务器因超时⽽断开连接,客户端仍然保持CONNECTING状态。如果因⽹络分区问题导致客户端与ZooKeeper集合被隔离⽽发⽣连接断开,那么其状态将会⼀直保持,直到显式地关闭这个会话,或者分区问题修复后,客户端能够获悉ZooKeeper服务器发送的会话已经过期。发⽣这种⾏为是因为ZooKeeper集合对声明会话超时负责,⽽不是客户端负责。直到客户端获悉ZooKeeper会话过期,否则客户端不能声明自⼰的会话过期。然⽽,客户端可以选择关闭会话。
客户端会尝试连接哪⼀个服务器?
在仲裁模式下,客户端有多个服务器可以连接,⽽在独立模式下,客户端只能尝试重新连接单个服务器。在仲裁模式中,应用需要传递可用的服务器列表给客户端,告知客户端可以连接的服务器信息并选择⼀个进⾏连接。
当尝试连接到⼀个不同的服务器时,⾮常重要的是,这个服务器的ZooKeeper状态要与最后连接的服务器的ZooKeeper状态保持最新。客户端不能连接到这样的服务器:它未发现更新⽽客户端却已经发现的更新。
参考资料
《Zookeeper分布式过程协同技术详解》