ZooKeeper是一个分布式的,开放源码的分布式应用程序协调服务,是Hadoop和Hbase、Storm的重要组件。本文将从ZooKeeper的主要特性、Zab协议、应用场景等方面来介绍Zookeeper的原理与应用。
1
ZooKeeper主要特性
数据模型
■层次化的目录结构,命名符合常规文件系统规范
■每个节点Znode都有唯一的路径标识
■Znode有两种类型,短暂的(ephemeral)和持久的(persistent)
•节点Znode可以包含数据和子节点
•短暂Znode的客户端会话结束时,zookeeper会将该短暂znode删 除,短暂Znode不可以有子节点
•持久Znode不依赖于客户端会话,需客户端明确删除
■Znode有四种形式的目录节点,PERSISTENT、PERSISTENT_SEQUENT IAL、EPHEMERAL、EPHEMERAL_SEQUENTIAL
■Znode节点数据支持多版本和ACL访问控制
读写机制
■Zookeeper是一个由多个server组成的集群
■一个leader,多个follower
■每个server保存一份数据副本
■全局数据一致
■分布式读写
■更新请求转发,由leader实施
•更新请求顺序进行,来自同一个client的更新请求按其发送顺序依次执行
•数据更新原子性,一次数据更新要么成功,要么失败
•实时性,在一定事件范围内,client能读到最新数据
监听器
■Watcher 在 ZooKeeper是一个核心功能
■Watcher 可以监控目录节点的数据变化以及子目录的变化,数据变化时,服务器就会通知所有设置在这个目录节点上的 Watcher
■可以设置观察的操作:exists,getChildren,getData
■可以触发观察的操作:create,delete,setData
■写操作与watcher的对应关系
角色
■领导者(leader),负责进行投票的发起和决议,更新系统状态Ø学习者(learner),包括跟随者(follower)和观察者(observer ),follower用于接受客户端请求并想客户端返回结 果,在选主过程中参与投票
■Observer可以接受客户端连接,将写请求转发给leader,但 observer不参加投票过程,只同步leader的状态,observer的目的 是为了扩展系统,提高读取速度
■客户端(client),请求发起方
2
Zab协议浅析
■Zookeeper的核心是原子广播,这个机制保证了各个server之间的同步。
■实现这个机制的协议叫做Zab协议。Zookeeper Atomic BroadcastØZab协议有两种模式,它们分别是恢复模式和广播模式。
■恢复模式
•当服务启动或者在领导者崩溃后,Zab进入恢复模式
•当领导者被选举出来,且大多数server的完成了和leader的状态同步以后,恢复模式就结束了。
•状态同步保证了leader和server具有相同的系统状态。
■广播模式
•当一个server加入zookeeper服务中,它会在恢复模式下启动,发现leader,并和leader进行状态同步。
•待到同步结束,它也参与消息广播。
•Zookeeper服务一直维持在Broadcast状态,直到leader崩溃了或者leader失去了大部分的followers支持。
Leader选举
■每个Server启动以后都询问其它的Server它要投票给谁。
■对于其他server的询问,server每次根据自己的状态都回复自己推荐的leader的id和上一次处理事务的zxid(系统启动时每个server都会推荐自己)
■收到所有Server回复以后,就计算出zxid最大的哪个Server,并将这个Server相关信息设置成下一次要投票的Server。
■计算这过程中获得票数最多的的sever为获胜者,如果获胜者的票数超过半数,则改server被选为leader。否则,继续这个过程,直到leader被选举出来。
■为什么zookeeper集群的数目,一般为奇数个?
•集群中只要有过半的机器是正常工作的,那么整个集群对外就是可用的。
•也就是说如果有2个zookeeper,那么只要有1个死了zookeeper就不能用了,因为1没有过半,所以2个zookeeper的死亡容忍度为0;
•同理,要是有3个zookeeper,一个死了,还剩下2个正常的,过半了,所以3个zookeeper的容忍度为1;
•同理你多列举几个:2->0;3->1;4->1;5->2;6->2会发现一个规律,2n和2n-1的容忍度是一样的,都是n-1,所以为了更加高效,何必增加那一个不必要的zookeeper呢。
3
应用场景
统一命名服务
■分布式应用中,通常需要有一套完整的命名规则。
■树形的名称结构是一个有层次的目录结构,既对人友好又不会重复。
■Name Service 是 Zookeeper 内置的功能,只要调用 Zookeeper 的 API 就能实现
配置管理
■将配置信息保存在 Zookeeper 的某个目录节点中
■所有服务对该节点监听Watcher,配置更新时,通知重新获取配置数据
集群管理
■如有多台 Server 组成一个服务集群,至少一个“总管”要知道当前集群中每台机器的服务状态,一旦有机器不能提供服务,集群中其它Sever必须知道,从而做出调整重新分配服务策略。
■同样当增加集群的服务能力时,就会增加一台或多台 Server,同样也必须让“总管”知道。
■zk.create("/testRootPath/testChildPath1","1".getBytes(), Ids.OPEN_ACL_UNSAFE,CreateMode.EPHEMERAL_SEQUENTIAL);
■zk.create(“/testRootPath/testChildPath2”,“2”.getBytes(), Ids.OPEN_ACL_UNSAFE,CreateMode.EPHEMERAL_SEQUENTIAL);
■zk.create("/testRootPath/testChildPath3","3".getBytes(), Ids.OPEN_ACL_UNSAFE,CreateMode.EPHEMERAL_SEQUENTIAL);
■zk.create("/testRootPath/testChildPath4","4".getBytes(), Ids.OPEN_ACL_UNSAFE,CreateMode.EPHEMERAL_SEQUENTIAL);
■System.out.println(zk.getChildren("/testRootPath", false));
■打印结果:[testChildPath10000000000, testChildPath20000000001, testChildPath40000000003, testChildPath30000000002]
■规定编号最小的为master
■master宕机的时候,相应的znode会消失
■新的服务器列表被推送到客户端,此时最小编号节点为master,这样就做到动态master选举。
共享锁
■共享锁在同一个进程中很容易实现,但是在跨进程或者在不同 Server 之间就比较复杂。
■Zookeeper 实现方式是需要获得锁的 Server 创建一PHEMERAL_SEQUENTIAL 目录节点
■getChildren方法获取当前的目录节点列表中最小的目录节点是不是就是自己创建的目录节点,如果正是自己创建的,那么它就获得了这个锁
■如果不是那么它就调用 exists(String path, boolean watch) 方法并监控 Zookeeper 上目录节点列表的变化,一直到自己创建的节点是列表中最小编号的目录节点
■释放锁很简单,只要删除它自己所创建的目录节点就可
队列管理
■同步队列:所有队列的成员到齐,才执行队列,否则一直等待。a)创建一个父目录 /synchronizing,每个成员都监控目录 /synchronizing/start 是否存在
b)每个成员都加入这个队列(创建 /synchronizing/x_i的临时目录节点)
c)每个成员获取 / synchronizing 目录的所有目录节点,判断 i的值是否已经是成员的个数d)如果小于成员个数等待 /synchronizing/start 的出现,如果已经相等就创建 /synchronizing/start
■FIFO队列:先入先出队列。a)/queue-fifo的目录下创建 SEQUENTIAL 类型的子目录 /x(i),这样就能保证所有成员加入队列时都是有编号的,b)出队列时通过 getChildren( ) 方法可以返回当前所有的队列中的元素,然后消费其中最小的一个,这样就能保证FIFO
4
结语
本次Zookeeper就介绍到这里,对数据一致性有兴趣的小伙伴可扩展阅读,深入了解一下一致性算法,如二阶提交协议、三阶提交协议、Paxos算法、Raft算法等。
网友评论