1.What is zookeeper?
ZooKeeper, a service for co-ordinating processes of distributed applications.Since ZooKeeper is part of critical infrastructure, ZooKeeper aims to provide a simple and high performance kernel for building more complex coordination primitives at the client.
2.Zookeeper overview
Zookeeper给其client呈现的是按层次组织的节点(znode),组织方式与文件系统类型,如下图,每个znode中可以包含一些数据。Zookeeper中有两种类型的znode,Regular和Ephemeral。对于Regular znode,其由client显式的创建和删除;对于Ephemeral znode,其由client创建,可由client显式删除,或当创建该Ephemeral znode的session终止时由zookeeper自动删除。
关于zookeeper中的session,当client连接zookeeper时,会初始化一个session,session有一个超时时间,如在超时时间内,zookeeper没有从client收到任何信息(zookeeper会发状态检测信息),则认为client故障了,此时zookeeper会关闭这个session(session也可由client显式关闭)。
另外,client在创建znode时还可以指定一个sequential flag,创建的znode将会拥有一个递增的序号,该序号会加到znode的名字后面。
Zookeeper实现了watch机制,允许client在watch的节点发生变化时得到通知(而不是轮询获取变化信息)。当client执行读操作(exsit,getData,getChildren等)时,可设置一个watch flag,这些操作除了给用户返回正常信息外,还会在返回给client的信息发生变化时通知client。一个Watch只会被触发一次(one-time trigger),当watch被触发或session关闭时,watch也会随之消失(unregistered)。
3.Zookeeper API
0. 构造一个zookeeper client实例,需要制定一个服务器列表(以逗号分割的ip:port列表),并制定session超时时间及默认的watch。
1. create(path, data, flags) :创建一个由path指定的znode,该znode中存储data,flag用于设置znode的类型(regular、ephemeral、sequential..),返回新建znode的名字。虽然znode具有存储功能,但强烈不建议将其用于存储大量数据,通常znode中存储一些metadata。
2. delete(path, version) : 如果znode的version与参数中version相同,删除由path指定的znode。
3. exists(path, watch) : znode是否存在,如果设置watch标记,则返回的信息变化时会通知client(不存在—>存在,存在—>不存在两种变化都会通知)。
4. getData(path, watch) : 获取znode的data,可设置watch标志(当该zonde的data变化时得到通知),如果znode不存在watch不会被设置。
5. setData(path, data, version) : 设置znode的data,与delete相同需要检查version信息。
6. getChildren(path, watch) : 获取子节点的名字列表,可设置watch(当创建或删除子节点时会得到通知)。
7. sync(path) : 等待所有的pending update被执行。
Zookeeper的API接口拥有同步和异步两个版本,使用异步API时,client可为每个operation设置callback,在operation被执行后,zookeeper会执行对应的callback。
Zookeeper所有的update接口都带version信息(-1表示不检查version信息),用于实现condition update,在update前检查version是否匹配,只有在version信息匹配时,update才会被更新,有点类似与CAS。
4.Zookeeper guarantees
zookeeper对client的请求做如下保证:
1. Linearizable writes: all requests that update the stateof ZooKeeper are serializable and respect prece-dence;
2. FIFO client order: all requests from a given client are executed in the order that they were sent by the client.
5.Examples of primitives
1. 配置项管理
分布式系统中,经常会有多个节点共享一份配置信息,并且配置信息可能动态的发生变化,此时需要节点能在配置变化时动态加载,通过zookeeper可很方便的完成。将配置信息存储在zookeeper的某个节点C上(可能包含很多配置子项),进程启动时先连接zookeeper获取C上的配置信息并注册watch,当配置信息发生变化时会得到通知,此时进程重新加载配置。
2. 主从配合
分布式系统中,多个进程可能需要协作,某个进程在启动时需要知道其他进程的一些信息,而这些信息可能会动态变化。如某系统中,包含一个master进程和多个worker进程,worker在启动时必须连接知道master的运行的一些信息(如ip:port,状态等),而master的运行由调度器完成,可能动态变化,通过zookeeper可完成这类需求。在zookeeper上创建一个节点R,当master启动时,将其运行信息写入R节点,当worker启动时,读取R的信息并设置watch,如果R中有信息,则读取并启动;如果没有,当master向R写入信息后,worker会被通知,此时读取信息并且启动worker;另外,可以将R设为Ephemeral并注册deletewatch时间,当R不存在时,所有的进程退出。
3. 组管理
组管理在分布式系统中很常见,如某个分布式存储系统,包含多个存储节点,这多个存储节点构成一个group,合理管理group是系统运行的关键,如监控并处理group中节点加入(扩展性)和退出(容错)事件,通过zookeeper也可方便实现组管理功能。用一个节点G代表group,系统在group上设置watch,当有节点加入时,在G下创建一个临时子节点,当节点退出时,临时子节点也会被删除,系统只需要根据相应的事件类型进行处理即可。
4. 分布式锁
解决方案依然很简单,需要加锁的进程先尝试在zookeeper上创建一个临时节点L,如果创建成功则加锁成功,如果不成功(已存在)则在该节点上设置watch。进程通过删除L来解锁(当进程意外终止,L也会被删除,不会造成死锁),当L被删除时,其它等待锁的进程会得到通知,此时这些进程再次创建L来获得锁。
上面的方案,当竞争锁的进程比较多时,解锁时会引起Herd Effect,可对加锁规则进行限制,如按进程尝试加锁的顺序来分配锁。在zookeeper上,每个加锁的进程创建一个带SEQUENTIAL标志的临时节点,每次让序号最小的节点获得锁,这样每个节点只需要watch它前面节点的状态即可,当其前面节点被删除时,其将被通知,并获得锁。改进如下:
如果要实现读写锁,则要做进一步改进,要获得写锁的进程按上述方式竞争锁,要获得读锁的进程则watch序号比自己小的写进程,改进如下:
5. 执行屏障
类似于linux中的barrier,有时进程只要在满足一定条件才能继续执行,如某个需要启动多个工作进程,进程中的某个位置,只有进程达到一定数目时才能继续(越过该barrier)。Zookeeper的解决方案时,创建一个任务节点T,当进程启动时,在T下注册一个子节点,当T下子节点的数目达到指定数目即可越过barrier。
参考:
[1] ZooKeeper: Wait-free coordination for Internet-scale systems