zookeeper 简单小节
1. ZooKeeper 是什么
ZooKeeper是一个分布式的,开放源码的分布式应用程序协调服务。主要用来解决分布式集群中应用系统的一致性问题,它能提供基于类似于文件系统的目录节点树方式的数据存储,但是 Zookeeper 并不是用来专门存储数据的,它的作用主要是用来维护和监控存储的数据的状态变化。分布式应用程序可以基于它实现同步服务,配置维护和命名服务等。
ZooKeeper的数据模型是一个树形节点,服务启动后,所有通过把数据加载到内存中,来提高服务器吞吐并减少延迟。
2. 如何使用:
前面说到,ZooKeeper的作用就是用来维护和监控数据模型(目录节点树)中的数据的状态,所以操作就像操作目录一样:添加删除目录节点,添加删除子目录,设置权限,设置数据,监控目录的变化等。
- zkCli 命令行使用:
- 创建:创建节点,并设置数据,创建标识有两类:持久化目录节点(PERSISTENT),顺序性目录节点(SEQUENTIAL)共四种组合。
-
create [-s] [-e] PATH DATA acl # 路径和数据,数据可以为空 -s是顺序节点(会自动在名称后面加序列号) -e是临时节点,不加则是永久节点
$ create /zktest/test1 test1
-
- 删除:
-
1 delete PATH [version] # 删除指定路径的节点,且节点必须为空,如果要删除含有子节点的节点则不行 2 rmr PATH # 递归删除
-
- 限额:
-
setquota -n|-b val path # -n 是限制子节点个数 -b是限制节点数据长度 val是额度 path 是节点 delquota [-n|-b] PATH # 删除配额设置 listquota PATH # 显示节点配额信息
-
- acl 权限:
- 给某个目录节点重新设置访问权限,需要注意的是 Zookeeper 中的目录节点权限不具有传递性,父目录节点的权限不能传递给子目录节点。目录节点 ACL 由两部分组成:perms 和 id。
Perms 有 ALL、READ、WRITE、CREATE、DELETE、ADMIN 几种
而 id 标识了访问目录节点的身份列表,默认情况下有以下两种:
ANYONE_ID_UNSAFE = new Id("world", "anyone") 和 AUTH_IDS = new Id("auth", "") 分别表示任何人都可以访问和创建者拥有访问权限。
- 给某个目录节点重新设置访问权限,需要注意的是 Zookeeper 中的目录节点权限不具有传递性,父目录节点的权限不能传递给子目录节点。目录节点 ACL 由两部分组成:perms 和 id。
- 设置
-
set PATH DATA [version] # 路径和数据,可以设置版本号也可以不设置
-
- 创建:创建节点,并设置数据,创建标识有两类:持久化目录节点(PERSISTENT),顺序性目录节点(SEQUENTIAL)共四种组合。
- Python 使用(本例子使用 Python3, kazoo,也可以使用 zkpython):
-
# -*- coding: utf-8 -*- import sys, time import kazoo from kazoo.client import KazooClient from kazoo.client import DataWatch from kazoo.client import ChildrenWatch class ZkWatcher(object): def __init__(self, node, hosts="127.0.0.1:2181", timeout=15.0): self._hosts = hosts self._timeout = timeout self._node = node self._zk = KazooClient(hosts=self._hosts, timeout=timeout or self._timeout) self._node_children_list = [] def connect(self, timeout=15.0): if self._zk is None: self._zk = KazooClient(hosts=self._hosts, timeout=timeout or self._timeout) self._zk.start() def close(self): if self._zk: self._zk.stop() self._zk.close() self._zk = None def watcher_start(self): self.connect() self._node_children_list = self._zk.get_children(self._node) try: ChildrenWatch(self._zk, self._node, func=self._node_children_change) DataWatch(self._zk, path=self._node, func=self._node_data_change) while True: time.sleep(60) except Exception as e: print(e.args) def _node_children_change(self, children): len_now = len(self._node_children_list) if len(children) > len_now: print("add:", set(children) - set(self._node_children_list)) else: print("remove: ", set(self._node_children_list) - set(children)) self._node_children_list = children def _node_data_change(self, data, stat): print("更新长度{},内容:{}".format(stat.data_length, data.decode("utf-8"))) print("版本号:", stat.version) print("cversion:", stat.cversion) print("子节点数:", stat.numChildren) def register(self): """ register a node for this worker """ if not self._zk.exists(self._node): self._zk.create(self._node, bytes("%s" % time.time()), ephemeral=False, sequence=True) if __name__ == "__main__": zw = ZkWatcher("/zktest", hosts="192.168.0.189:2181") zw.watcher_start()
-
- 介绍:
-
ZK中的时间和版本号:
- ZXID:ZK节点状态改变会导致该节点收到一个zxid格式的时间戳,这个时间戳是全局有序的,每次更新都会产生一个新的。如果zxid1的值小于zxid2,那么说明zxid2发生的改变在zxid1之后。zxid是一个唯一的事务ID,具有递增性,一个znode的建立或者更新都会产生一个新的zxid值,具体时间有3个cZxid(节点创建时间)、mZxid(该节点修改时间,与子节点无关)、pZxid(该节点的子节点的最后一次创建或者修改时间,孙子节点无关)
- version:对节点的每次操作都会使节点的版本号增加,有三个版本号dataversion(数据版本号)、cversion(子节点版本号)、aclversion(节点所拥有的ACL版本号)
-
cZxid 创建节点时的事务ID ctime 创建节点时的时间 mZxid 最后修改节点时的事务ID mtime 最后修改节点时的时间 pZxid 表示该节点的子节点列表最后一次修改的事务ID,添加子节点或删除子节点就会影响子节点列表,但是修改子节点的数据内容则不影响该ID cversion 子节点版本号,子节点每次修改版本号加1 dataversion 数据版本号,数据每次修改该版本号加1 aclversion 权限版本号,权限每次修改该版本号加1 dataLength 该节点的数据长度 numChildren 该节点拥有子节点的数量
-
3. 为什么这么用,应用场景
ZooKeeper 从设计模式角度来看,是一个基于观察者模式设计的分布式服务管理框架,它负责存储和管理大家都关心的数据,然后接受观察者的注册,一旦这些数据的状态发生变化,ZooKeeper 就将负责通知已经在 ZooKeeper 上注册的那些观察者做出相应的反应,从而实现集群中类似 Master/Slave 管理模式。
主要用于通用性配置,比如:机器列表,某些参数的开关。拥有以下特点:
- 传递的信息体量小
- 供应用服务动态调整
- 某一组应用使用相同的配置
用于分布式去中心化集群管理:如有多台 Server 组成一个服务集群,那么必须要一个“总管”知道当前集群中每台机器的服务状态,一旦有机器不能提供服务,集群中其它集群必须知道,从而做出调整重新分配服务策略。同样当增加集群的服务能力时,就会增加一台或多台 Server,同样也必须让“总管”知道,调整分配。有两个作用:
- 维护集群中服务器的状态
- 自动选出集群中的‘总管’Master
Leaders 的选举基于自己的ZAB算法,即原子消息广播协议。ZAB协议包括两种基本模式,崩溃恢复和消息广播,大致过程:
- => 恢复模式:集群启动过程中,Leader断开、崩溃退出或重启等异常情况,ZAB会进入恢复模式并选举新的Leader,当产生了新的Leader后并集群中过半腐恶去完成了与Leader的状态同步(数据同步),那么ZAB协议退出恢复模式,进入消息广播模式。
- => 广播模式:ZooKeeper使用一个单一的主进程来接收客户端的事务请求会生成一个事务提案并发起广播,是Leader服务器的话;如果非Leader服务器收到客户端请求后它会把请求转发给Leader服务器。。
- 广播=>恢复:如果在当前集群中新加入一台服务器,那么这台新服务器会自动进入恢复模式,待完成与集群Leader的同步之后就进入消息广播模式
4. 原理:
- 原子消息广播协议
-
- 消息广播:
在广播事务之前Leader服务器会先给这个事务分配一个全局单调递增的唯一ID,也就是事务ID(ZXID),每一个事务必须按照ZXID的先后顺序进行处理。而且Leader服务器会为每一个Follower分配一个单独的队列,然后将需要广播的事务放到队列中。每个Follower服务器再接收到这个事务之后,都会将其以事务日志的形式写入到本地磁盘中,成功写入后反馈给Leader一个ACK,当Leader收到半数ACK响应之后,就会广播一个Commit消息给所有Follower,通知它们进行提交,同时Leader也会完成自身的提交。
-
- 崩溃恢复:
其目的就是保证尽快选举出一个新的Leader并通知给其他Follower,同时保证整个集群中的数据状态是一致的
2 节点数据管理:树型文件存储系统以及通过客户端与ZK建立一个TCP的长连接来维持会话,通过这个连接可以检测心跳与服务器保存会话,也可以发送请求并接受服务器响应,也可以接受WATCH事件。
5. 参考: