深入理解zookeeper——基础概念

znode节点

概述

很多用于协作的原语常常在很多应用之间共享,因此设计一个用于协作需求的服务方法往往是提供原语列表,暴露每个原语的实例化调用方法,并直接控制这些实例。比如分布式锁机制原语的创建,获取和释放三个API。但这样存在一些缺陷:首先要么预先提出一份详尽的原语列表,要么提供API的扩展,以便引入新的原语;其次这样做的灵活性不是很好。zookeeper并不直接暴露自己的原语,它暴露了由一小部分调用方法组成的类似文件系统的API,以便允许应用创建自己的原语。

zookeeper使用类似文件系统的层级树状结构进行管理,组成这棵树的节点也叫znode,其中,叶子节点存储数据信息。

  • worker节点作为父节点,其下每个znode子节点保存了一个可用从节点的信息。
  • tasks节点作为父节点,其下每个znode节点保存了所有已经创建并等待从节点执行的任务信息。
  • asign节点作为父节点,其下每个znode节点保存了分配到某个从节点的一个任务信息。当主节点给某个从节点分配了一个任务,就会在asign下增加一个子节点。

需要注意的是zookeeper不允许局部写入或读取znode节点的数据,当设置一个znode节点的数据或读取时,znode节点的内容会被整个替换或读取

类型

新建znode节点时需要指定节点类型,不同的类型有不同的行为。

  1. 持久节点和临时节点
    znode是持久类型的节点时,一般用它保存一些数据,即使znode的创建者不再属于应用系统时,数据也可以继续保存。比如主从模式保存从节点的任务分配情况,即使主节点已经崩溃了。
    临时znode一般用于传达应用某方面的信息,当创建者的会话有效时,这些信息必须保存;当会话过期时,应该及时删除该临时节点。
    删除一个持久节点只能通过创建者发送命令来删除。而删除一个临时节点可以是客户端(不一定是创建者)主动删除,也可以是创建该znode的客户端的会话因超时或主动关闭而终止。
  2. 有序节点
    一个有序的znode节点会被分配唯一一个单调递增的整数。当创建有序节点时,一个序号会被追加到路径之后。也就是说,有序znode提供了创建具有唯一名称的znode,也因此可以直观的看出各znode的创建顺序。
    综上,节点的类型共有4种:持久的,临时的,持久有序的,临时有序的

监视与通知

zookeeper通常以远程服务的方式被访问,如果每次访问znode时客户端都要获得节点中的内容(轮询访问),代价无疑非常大,因为存在很高的延迟,而且zookeeper也要做很多工作。为了替换客户端的轮询,zookeeper使用了基于通知的机制:客户端向zookeeper注册需要接收通知的znode,通过对znode设置监视点来接收通知。监视点是一个单次触发的操作,意即监视点会触发一个通知。如果要接收多个通知,客户端必须在每次通知后设置一个新的监视点。需要注意的是:因为通知机制是单次触发的操作,所以在客户端接受了一个znode变更通知并设置新的监视点时,znode节点也许发生了新的变化。(这个类似Linux编程下的信号量,在注册信号处理函数前如果信号就发生了,那么处理函数将无法运行)为了避免这种情况的发生,zookeeper采用的机制是:在设置新的监视点前,客户端读取监视节点的状态,在设置监视点之前,读取zookeeper的状态。
每一个znode都有一个版本号,它随着每次数据变化而自增。两个API操作可以有条件的执行:setData和delete。这两个API以版本号作为传入参数,只有当传入参数的版本号与服务器上的版本号一致时调用才会成功。(这个也可以类比Linux编程的多线程编程中对同一个全局变量或静态变量进行读写操作时会导致的混乱)。

客户端c1对znode/config写入了一些配置信息,如果另一个客户端c2同时更新了这个znode,此时c1的版本号已经过期,c1调用setData肯定不会成功。

zookeeper架构

应用通过客户端库来对zookeeper实现调用,客户端库负责与zookeeper服务器端进行交互。不要被翻译误导,客户端库不是所有客户端的集合,它是一个库,每一个客户端导入客户端库,之后便可以与任何zookeeper的节点进行通信。

运行模式

zookeeper服务器端运行与两种模式下:独立模式仲裁模式。独立模式与其描述一样:有一个单独的服务器,zookeeper状态无法复制。仲裁模式下具有一组zookeeper服务器,称为zookeeper集合,它们之间可以进行状态的复制,并同时可以服务客户端的请求。
仲裁模式下,zookeeper复制集群中的所有服务器的数据树。但如果让一个客户端等待每个服务器完成数据保存后再继续,延迟问题会很突出。所以zookeeper定义了法定人数概念(绝对是翻译问题…)。法定人数表示服务器告知客户端安全保存数据前,需要保存客户端数据的服务器的最小个数,一般其数值为floor(服务器总数/2)+1。该值需要保证不管系统发生延迟或者崩溃,服务主动确认的任何更新请求需要保持下去,直到另一个请求替代它。这里的服务器数量建议取奇数,因为偶数会使得系统更加脆弱。比如4台服务器,那么法定人数为3,仅允许1台服务器崩溃,这意味着对于每个请求,需要做更多的确认操作。

会话

在对zookeeper集合执行任何请求前,一个客户端必须先与服务建立会话。客户端提交给zookeeper的所有操作均关联在一个会话上。当一个会话因为某种原因终止时,在这个会话期间创建的临时节点将会消失,也可能该会话会被客户端库透明的转移到另一台服务器上,这取决于客户端初始连接到独立的服务器还是服务器集合的某一台上。
此外,会话还可以提供顺序保障,同一个会话中的所有请求都会以FIFO的方式顺序执行。通常,客户端只打开一个会话,因此客户端请求都会以FIFO的方式执行,如果客户端拥有多个并发的会话,FIFO方式未必能够保持。

每个会话都会有生命周期,在创建一个会话时,需要设置会话超时这个参数,这个参数表示zookeeper服务允许会话被声明为超时之前存在的时间。如果经过t之后服务接收不到这个会话的任何消息,服务就会声明会话过期。而在客户端侧,如果经过t/3的时间没有收到消息,客户端向服务器发送心跳消息。在经过2t/3时间后,zookeeper客户端开始寻找其他的服务器,而此时它还有t/3时间去寻找。

主从模式示例

主从模式的模型中包括三个角色:主节点,从节点,客户端。
主节点负责监视新的从节点和任务,分配任务给可用的从节点。
从节点会通过系统注册自己,以确保主节点看到他们可以执行任务,然后开始监视新任务。
客户端创建新任务并等待系统的响应。


参考资料:

《zookeeper分布式过程协同技术详解》