Zookeeper是分布式一致性问题的工业解决方案,是Apache Hadoop下解决分布式一致性的一个组件,后被分离出来成为Apache的顶级项目。
工程来源:是雅虎公司内部项目,据说雅虎内部很多项目都是以动物命名,这个动物管理员的名字起的很是形象。
被开源出来后得到开源社区的快速推进,服务端Java语言实现,棒,git有3000+的star:
https://github.com/apache/zookeeper
zookeeper集群结构
集群的角色,比较典型的是Master/Slave(主备模式),zk中的概念跟这个不一样,包含Leader、Follower、Observer三个角色,leader提供读和写的能力,follower只对外提供读的能力。
会话(session)
客户端跟服务端交互,是先与服务端建立一个TCP长连接,会话开始,通过心跳检测与服务端保持会话有效,向服务端发送请求和接收响应。
zk将所有的数据都加载在内存一份,同时有事务日志文件(持久化文件),服务端会定时dump快照数据,重启机器的时候会根据快照和事务日志恢复内存数据库的数据,这跟redis的AOF和RDB概念类似。
zookeeper上的数据结构
zk上的数据的结构跟linux文件系统很像,是个树状结构
节点(node)上的信息字段
节点类型包括:
其中临时节点特性就是创建它的主体消失后,它就跟着消失了。后续的应用就是利用的节点的特性实现的。
事件监听器(watcher)
这个应该是zookeeper最重要的概念之一了,zk允许用户在特定的节点(znode)上注册watcher,并且在特定事件触发的时候,zk服务端会将事件通知到感兴趣的客户端上。
伪集群的搭建:
zoo.cfg 配置文件
启动成功后,命令行连接zk,可以用指令做些增删改查的操作
telnet 127.0.0.1 2181
stat:可以看集群的状态信息
stat信息
每次事务操作,会在dataDir的目录下的事务日志,是序列化的二进制文件,zookeeper提供了查看事务日志的工具类LogFormatter
LogFormatter转换后的事务日志文件
Java客户端使用
cruator客户端例子
zookeeper应用场景
利用zookeeper的特性,可以比较方便的构建分布式应用会涉及的核心功能,比如:配置中心、命名服务、分布式协调/通知、集群的管理、master选举、分布式锁等
以下应用基本基于zookeeper的两大特性实现
--配置中心:
配置中心
zookeeper利用推拉结合的方式,客户端向服务端注册自己需要关注的节点,一旦该数据发生变更,那么服务器就会向相应的客户端发送watcher时间通知。
客户收到这个消息通知之后,再主动到服务端获取最新的数据。即回调的event中包含具体的数据。
这个应用的的业务员特点:
--命名服务
利用zookeeper的顺序节点,树形结构的数据特点,实现命名服务:
这样RPC的客户端只需要传对应的服务名字,和接口,就能找到对应的服务。
使用zookeeper实现:不同的业务下创建一个节点,具体的节点下用zk的顺序节点(sequent)生成id当做这个业务的全局唯一id使用
--分布式通知/协调
ZooKeeper中特有watcher注册与异步通知机制,能够很好的实现分布式环境下不同系统之间的通知与协调,实现对数据变更的实时处理。
使用方法通常是不同系统都对ZK上同一个znode进行注册,监听znode的变化(包括znode本身内容及子节点的),其中一个系统update了znode,那么另一个系统能够收到通知,并作出相应处理。
应用:
心跳检测机制:传统的方式是ping,复杂的话是建立长连接检测系统和被检测系统之间并不直接关联起来,而是通过zookeeper上某个节点关联,大大减少系统耦合
系统调度模式:某系统由控制台和推送系统两部分组成,控制台的职责是控制推送系统进行相应的推送工作。
管理人员在控制台作的一些操作,实际上是修改了ZK上某些节点的状态,而ZK就把这些变化通知给他们注册Watcher的客户端,即推送系统。于是,作出相应的推送任务
作汇报模式:一些类似于任务分发系统,子任务启动后,到ZK来注册一个临时节点,并且定时将自己的进度进行汇报(将进度写回这个临时节点)
总之,使用zookeeper来进行分布式通知和协调能够大大降低系统之间的耦合。
--分布式锁
这个应用主要得益于ZooKeeper为我们保证了数据的强一致性
即用户只要完全相信每时每刻,zk集群中任意节点(一个zk server)上的相同znode的数据是一定是相同的。
一个节点要么创建成功,要么失败,并且只由一个客户端创建。
独占锁:
保持独占,就是所有试图来获取这个锁的客户端,最终只有一个可以成功获得这把锁。
通常的做法是把ZK上的一个znode看作是一把锁,通过create znode的方式来实现。所有客户端都去创建/distribute_lock节点,最终成功创建的那个客户端也即拥有了这把锁。
共享时序控制锁:
Zookeeper很容易实现这个功能,实现方式是需要获得锁的Server,创建一个EPHEMERAL_SEQUENTIAL目录节点。
然后调用getChildren方法获取当前的目录节点列表中最小的目录节点是不是就是自己创建的目录节点。
如果正是自己创建的,那么它就获得了这个锁,如果不是,那么它就调用exists(String path, boolean watch)方法,并监控Zookeeper上目录节点列表的变化,一直到自己创建的节点是列表中最小编号的目录节点,从而获得锁。
释放锁很简单,只要删除前面它自己所创建的目录节点就行了。
--master选举
master选举应用图
有个容易理解的方案,依靠关系型数据库主键的特性,集群的机器同时往一张表里插入数据,数据库会自动进行主键冲突检查,可以选择插入成功的客户端作为master
这种方式存在一个问题就是,master机器挂了,没有人通知
zk实现可以方便做到这一点:zk的创建节点api接口,具有强一致性,能够保证客户端并发创建的时候,最终一定只有一个客户端创建成功。
--集群管理
应用举例:集群机器存活性监控系统,例如:
监控系统在/clusterServers节点注册一个watcher监听,那么但凡进行动态添加机器的操作,就在/clusterServers下创建一个临时节点, /clusterServers/ip。
这样监控系统就能够实时的检测到机器的变动,通过getChild方法获取所有的临时节点,来判断增加的机器。
当有机器down调或者手动下线,相应临时节点会消失,监控系统也会接收到,来处理监控服务的具体业务
具体服务器部署agent实现
zookeeper的HA设计实现
以上说了那么多犀利实用的应用场景,它们依赖zookeeper,说明这些应用服务的高可用性依赖的zookeeper本身的HA。
zk的选举算法
算法协议zab协议,“少数服从多数”协议一种
3.4.0版本之后Zookeeper只保留了TCP版本的FastLeaderElection选举算法
分析选举算法前,先熟悉了解下zk的一些术语定义解释:
集群数量是4,quorum=2,集群数量是5,quorum=3
当哪些情况发生时会出发leader重新选举呢?
当zk的一台服务器出现以下两种情况的时候,会进入leader选举流程
对于第一种情况,即已经存在一台leader服务器,当改机器试图去选举leader的时候,会被告知当前服务器的leader信息,对于该机器仅仅需要和leader建立连接,并进行状态同步即可
主要看下第二种情况:
有两种情况导致集群不存在leader,一个是集群刚启动初始化的时候,另一种情况是运行期间leader所在服务器挂了。
无论哪种情况集群所有集群都处在一个找leader的状态,称作Looking状态,开始想其他机器发送消息投票
开始leader选举投票的协议规则是怎样呢?
5台机器宕机两台后,leader选举的过程图示
因此,一个错误的认识,为了使zookeeper集群能顺利的选出leader,必须将zookeeper集群的服务器数部署为奇数。
从上边例子能看出来部署任意台机器都能够正常选举运行。部署奇数台是官方给的建议,因为奇数和奇数+1的容灾能力是一样的。比如:
5台服务器,能够对2台机器挂掉的情况进程容灾
6台服务器,能够对2台机器挂掉的情况进程容灾,如果挂掉3台,剩下的机器就无法实现过半了。