Zookeeper 是什么
Zookeeper 是一个分布式协调服务的开源框架, 主要用来解决分布式集群中应用系统的一致性问题, 例如怎样避免同时操作同一数据造成脏读的问题.
ZooKeeper 本质上是一个分布式的小文件存储系统. 提供基于类似于文件系统的目录树方式的数据存储, 并且可以对树中的节点进行有效管理. 从而用来维护和监控你存储的数据的状态变化. 通过监控这些数据状态的变化,从而可以达到基于数据的集群管理.
在大数据生态系统里,很多组件的命名都是某种动物,比如 hadoop 就是大象, hive就是蜜蜂, 而 Zookeeper 就是动物管理员.
Zookeeper 的数据模型
- ZK 本质上是一个分布式的小文件存储系统.
- ZK 表现为一个分层的文件系统目录树结构, 既能存储数据, 而且还能像目录一样有子节点. 每个节点可以存最多 1M 左右的数据.
- 每个节点称做一个 Znode, 每个 Znode 都可以通过其路径唯一标识.
- 而且客户端还能给节点添加 watch, 也就是监听器, 可以监听节点的变化, 这个功能常在实际开发中作为监听服务器集群机器上下线操作.
节点结构
每个节点称为一个 Znode。 每个 Znode 由 3 部分组成:
- stat:此为状态信息, 描述该 Znode 的版本, 权限等信息
- data:与该 Znode 关联的数据
- children:该 Znode 下的子节点
节点类型
Znode 有 2 大类 4 小类, 两大类分别为永久节点和临时节点.
- 永久节点(Persistent): 客户端和服务器端断开连接后,创建的节点不会消失, 只有在客户端执行删除操作的时候, 他们才能被删除.
- 临时节点(Ephemeral): 客户端和服务器端断开连接后,创建的节点会被删除.
Znode 还有一个序列化的特性, 这个序列号对于此节点的父节点来说是唯一的, 这样便会记录每个子节点创建的先后顺序. 它的格式为“%10d”(10 位数字, 没有数值的数位用 0 补充, 例如“0000000001”),因此节点可以分为 4 小类:
- 永久节点(Persistent)
- 永久_序列化节点(Persistent_Sequential)
- 临时节点(Ephemeral)
- 临时_序列化节点(Ephemeral_Sequential)
Zookeeper 的 watch 监听机制
- 在 ZooKeeper 中还支持一种 watch(监听)机制, 它允许对 ZooKeeper 注册监听, 当监听的对象发生指定的事件的时候, ZooKeeper 就会返回一个通知.
- Watcher 分为以下三个过程:客户端向 ZK 服务端注册 Watcher、服务端事件发生触发 Watcher、客户端回调 Watcher 得到触发事件情况.
触发事件种类很多,如:节点创建,节点删除,节点改变,子节点改变等。 - Watcher 是一次性的. 一旦被触发将会失效. 如果需要反复进行监听就需要反复进行注册.
监听器原理
首先要有一个 main()线程
在 main 线程中创建 Zookeeper 客户端, 这时就会创建两个线程, 一个复制网络连接通信(connect), 一个负责监听(listener).
通过 connect 线程将注册的监听事件发送给 zk, 常见的监听有
监听节点数据的变化 get path [watch]
监听节点状态的变化 stat path [watch]
监听子节点增减的变化 ls path [watch]
将注册的监听事件添加到 zk 的注册的监听器列表中
监听到有数据或路径变化, 就会将这个消息发送给 listener 线程.
listener 线程内部调用了 process()方法.此方法是程序员自定义的方法, 里面可以写明监听到事件后做如何的通知操作.
监听器实际应用
- 监听器+ZK 临时节点能够很好的监听服务器的上线和下线.
- 第一步: 先想 zk 集群注册一个监听器, 监听某一个节点路径
- 第二步: 主要服务器启动, 就去 zk 上指定路径下创建一个临时节点.
- 第三步: 监听器监听 servers 下面的子节点有没有变化, 一旦有变化, 不管新增(机器上线)还是减少(机器下线)都会马上给对应的人发送通知.
Zookeeper 的应用场景
ZK 提供的服务包括:
统一命名服务,统一命名服务使用的是 ZK 的 node 节点全局唯一的这个特点.
在分布式环境下,经常需要对应用/服务进行统一命名,便于识别。例如:IP 不容易记住,而域名容易记住。创建一个节点后, 节点的路径就是全局唯一的, 可以作为全局名称使用.
统一配置管理, 使用的是 Zookeeper 的 watch 机制
可以把所有的配置都放在一个配置中心, 然后各个服务分别去监听配置中心,一旦发现里面的内容发生变化, 立即获取变化的内容, 然后更新本地配置即可.
可将配置信息写入 Zookeeper 上的一个 Znode.
各个客户端服务器监听这个 Znode.
一旦 Znode 中的数据被修改, Zookeeper 将通知各个客户端服务器.
统一集群管理,统一集群管理使用的是 Zookeeper 的 watch 机制
可将节点信息写入 Zookeeper 上的一个 Znode
监听这个 Znode 可获取它的实时状态变化
集群选主,集群选主使用的是 zookeeper 的临时节点
所有参与选主的主机都去 Zookeeper 上创建同一个临时节点,那么最终一定只有一个客户端请求能够 创建成功。
成功创建节点的客户端所在的机器就成为了 Master,其他没有成功创建该节点的客户端,成为从节点
所 有 的 从 节 点 都 会 在 主 节 点 上 注 册 一 个 子 节 点 变 更 的Watcher,用于监控当前主节点是否存活,一旦 发现当前的主节点挂了,那么其他客户端将会重新进行选主。
服务动态上下线,
分布式锁等.分布式锁使用的是 Zookeeper 的临时有序节点
- 所有需要执行操作的主机都去 Zookeeper 上创建一个临时有序节点.
- 然后获取到 Zookeeper 上创建出来的这些节点进行一个从小到大的排序.
- 判断自己创建的节点是不是最小的, 如果是, 自己就获取到了锁; 如果不是,则对最小的节点注册一个监听.
- 如果自己获取到了锁, 就去执行相应的操作. 当执行完毕之后, 连接断开, 节点消失, 锁就被释放了.
- 如果自己没有获取到锁, 就等待, 一直监听节点是否消失,锁被释放后, 再重新执行抢夺锁的操作.
Zookeeper 集群介绍
ookeeper 在一个系统中一般会充当一个很重要的角色, 所以一定要保证它的高可用,这就需要部署 Zookeeper 的集群. Zookeeper 有三种运行模式: 单机模式, 集群模式和伪集群模式.
- 单机模式: 使用一台主机不是一个 Zookeeper 来对外提供服务, 有单点故障问题,仅适合于开发、测试环境.
- 集群模式: 使用多台服务器, 每台上部署一个 Zookeeper 一起对外提供服务, 适合于生产环境.
- 伪集群模式: 在服务器不够多的情况下, 也可以考虑在一台服务器上部署多个Zookeeper 来对外提供服务.
集群角色
- Leader: 负责投票的发起和决议, 更新系统状态, 是事务请求(写请求) 的唯一处理者,一个 ZooKeeper 同一时刻只会有一个 Leader.
对于 create 创建/setData 修改/delete 删除等有写操作的请求, 则需要统一转发给 leader处理, leader 需要决定编号和执行操作, 这个过程称为一个事务. - Follower: 接收客户端请求, 参与选主投票. 处理客户端非事务(读操作)请求,转发事务请求(写请求)给 Leader;
- Observer: 针对访问量比较大的 zookeeper 集群, 为了增加并发的读请求. 还可新增观察者角色.作用: 可以接受客户端请求, 把请求转发给 leader, 不参与投票, 只同步 leader 的状态.
Zookeeper 集群的特性
- Zookeeper: 一个领导者(Leader), 多个跟随者(Follower)组成的集群.
- 集群中只要有半数以上节点存活, Zookeeper 集群就能正常服务.
- 全局数据一致: 每个 Server 保存一份相同的数据副本, Client 无论连接到哪个 Server, 数据都是一致的.
- 更新请求顺序性: 从同一个客户端发起的事务请求,最终会严格按照顺序被应用到zookeeper 中.
- 数据更新原子性: 一次数据更新要么成功, 要么失败。
- 实时性,在一定时间范围内,Client 能读到最新数据。
ZAB 协议
- Zookeeper 采用 ZAB(Zookeeper Atomic Broadcast)协议来保证 分布式数据一致性 。
- ZAB 并不是一种通用的分布式一致性算法,而是一种专为 Zookeeper 设计的崩溃可恢复的原子消息广播算法。
- ZAB 协议包括两种基本模式: 崩溃恢复 模式和 消息广播 模式:
- 消息广播模式主要用来进行事务请求的处理
- 崩溃恢复模式主要用来在集群启动过程,或者 Leader 服务器崩溃退出后进行新的Leader 服务器的选举以及数据同步.
ZK 集群写数据流程
- Client 向 Zookeeper 的 Server1 上写数据, 发送一个写请求.
- 如果 Server1 不是 Leader, 那么 Server1 会把接受的请求进一步转发给 Leader, 因为每个 Zookeeper 的 Server 里面有一个是 Leader. 这个 Leader 会将写请求广播给各个Server, 比如 Server1 和 Server2, 各个 Server 会将该写请求加入待写队列, 并向Leader 发送成功信息(ack 反馈机制).
- 当 Leader 收到半数以上 Server 的成功信息, 说明该写操作可以执行. Leader 会向各个Server 发送事务提交信息, 各个 Server 收到信息后会落实队列里面的写请求, 此时写成功.
- Server1 会进一步通知 Client 数据写成功了, 这是就认为整个写操纵成功.
ZK 集群选举机制
- Zookeeper 服务器有四个状态:
- looking: 寻找 leader 状态, 当服务器处于该状态时, 它会认为当前集群中没有 leader,因此需要进入 leader 选举状态.
- leading: 领导者状态, 表明当前服务器角色是 leader.
- following: 跟随者状态, 表明当前服务器角色是 follower.
- observing:观察者状态, 表明当前服务器角色是 observer。
- 半数机制: 集群中半数以上机器存活, 集群可用, 所以 Zookeeper 适合安装奇数台服务器. 集群启动时, 如果当前机器票数超过了总票数一半则为 Leader, Leader 产生后, 投过票的机器就不能再投票了.
- Zookeeper 虽然在配置文件中没有指定主从节点. 但是, Zookeeper 工作时, 是有一个节点 Leader, 其他则为 Follower, Leader 是通过内部的选举机制临时产生的.配置文件中会指定每台 ZK 的 myid, 而且不能重复, 通常用 1,2,3…区分每台 ZK 的myid.
集群启动器的选举机制
在集群初始化阶段, 当有一台服务器 server1 启动时, 其单独无法进行和完成 leader选举, 当第二台服务器 server2 启动时, 此时两台机器可以相互通信, 每台机器都试图找到leader, 于是进入 leader 选举过程.
- 服务器 1 启动, 服务器 1 状态保持为 looking.
- 服务器 2 启动, 发起一次选举. 服务器 1 投票给比自己 ID 号大的服务器 2. 服务器 2 投票给自己.
投票结果: 服务器 1 票数 0 票, 服务器 2 票数 2 票, 没有半数以上结果, 选举无法完成,服务器 1, 2 状态保持 looking. - 服务器 3 启动, 发起一次选举. 此时服务器 1 和 2 都会更改选票为服务器 3, 服务器 3 投票给自己.
投票结果: 服务器 1 为 0 票, 服务器 2 为 0 票, 服务器 3 为 3 票. 此时服务器 3 的票数已经超过半数,服务器 3 当选 Leader. 服务器 1,2 更改状态为 follower,服务器 3更改状态为 leader; - 服务器 4 启动, 发起一次选举. 此时服务器 1,2,3 已经不是 looking 状态, 不会更改选票信息, 服务器 4 投票给自己.
投票结果:服务器 3 为 3 票,服务器 4 为 1 票。此时服务器 4 服从多数,更改选票信息为服务器 3,并更改状态为 following; - 服务器 5 启动,同 4 一样当小弟.
服务器运行时期的 Leader 选举
在 zk 运行期间, leader 与非 leader 服务器各司其职, 即便当有非 leader 服务器宕机或者新加入, 此时也不会影响leader. 但是一旦leader服务器宕机了, 那么整个集群将会暂停对外服务, 进入新一轮 leader 选 举, 其过程和启动时期的 Leader 选举过程基本一致.
假设正在运行的有 server1,server2,server3 三台服务器,当前 leader 是 server2,若某一时刻 leader 挂了, 此时便开始 leader 选举. 选举过程如下:
- 变更转态, server1 和 server3 变更为 looking 状态.
- 开始投票, 每台服务器投票给比自己 myid 大的机器, 没有比自己大的就投给自己.
- 这样 server3有2票, server1有1票, server3的票数超过了集群一半, 当选 leader, server1 变更状态 follower.
为什么 zookeeper 集群的数目,一般为奇数个?
容错
由于在增删改操作中需要半数以上服务器通过,来分析以下情况。2 台服务器,至少 2 台正常运行才行(2 的半数为 1,半数以上最少为 2),正常运行1 台服务器都不允许挂掉3 台服务器,至少 2 台正常运行才行(3 的半数为 1.5,半数以上最少为 2),正常运行可以允许 1 台服务器挂掉
4 台服务器,至少 3 台正常运行才行(4 的半数为 2,半数以上最少为 3),正常运行可以允许 1 台服务器挂掉
5 台服务器,至少 3 台正常运行才行(5 的半数为 2.5,半数以上最少为 3),正常运行可以允许 2 台服务器挂掉
通过以上可以发现,3 台服务器和 4 台服务器都最多允许 1 台服务器挂掉,5 台服务器和 6 台服务器都最多允许 2 台服务器挂掉但是明显 4 台服务器成本高于 3 台服务器成本,6 台服务器成本高于 5 服务器成本。这是由于半数以上投票通过决定的。
防脑裂
一个 zookeeper 集群中,可以有多个 follower.observer 服务器,但是必需只能有一个 leader 服务器。
集群互不通讯情况:
一个集群 3 台服务器,全部运行正常,但是其中 1 台裂开了,和另外 2 台无法通讯。3台机器里面 2 台正常运行过半票可以选出一个 leader。
一个集群 4 台服务器,全部运行正常,但是其中 2 台裂开了,和另外 2 台无法通讯。4台机器里面 2 台正常工作没有过半票以上达到 3,无法选出 leader 正常运行。
一个集群 5 台服务器,全部运行正常,但是其中 2 台裂开了,和另外 3 台无法通讯。5台机器里面 3 台正常运行过半票可以选出一个 leader。
一个集群 6 台服务器,全部运行正常,但是其中 3 台裂开了,和另外 3 台无法通讯。6台机器里面 3 台正常工作没有过半票以上达到 4,无法选出 leader 正常运行。
通过以上分析可以看出,为什么 zookeeper 集群数量总是单出现,主要原因还是在于第 2 点,防脑裂,对于第 1 点,无非是正本控制,但是不影响集群正常运行。但是出现第 2种裂的情况,zookeeper 集群就无法正常运行了。