3.5 Zookeeper实现原理
Zookeeper 服务有两种不同的运行模式.
- 一种是独立模式(standalone mode)
- 一种是复制模式(replicated mode)
3.5.1 独立模式
这种模式比较简单, 只有一个 Zookeeper 服务器. 比较适合于测试环境(也可以在单元测试中采用), 但是不能保证高可用和可恢复性.
3.5.2 复制模式
在生产环境中的 Zookeeper 通常以复制模式运行在一个计算机集群上, 这个计算机集群通常被称为一个集合体(ensemble).
Zookeeper 通过复制来实现高可用性, 只要集合体中半数以上(不包括半数)的机器处于可用状态, 它就能够提供服务.##
例如:
在一个有 5 台机器的集合体中, 任意 2 机器出现故障, 都可以保证服务继续, 因为剩下的 3 台机器超过了半数.
在 6 个机器的集合体中, 最多只能容忍 2 台机器出现故障, 因为如果 3 台机器出现故障, 剩下的 3 台就没有超过集合体的半数了.
处于这个原因, 一个集合体中通常包含奇数台机器.
从概念上来说, Zookeeper 是非常简单的: 他所要做的就是确保对 znode
树的每一个修改都会被复制到集合体中的超过半数的机器上.
如果有不到半数的机器出现故障, 则最少会有一台机器保存最新的状态, 其余的副本最终也会更新到这个状态.
然而, 这个简单的想法的实现却不简单. Zookeeper 使用了 Zab 协议, 该协议包括以下两个无限重复的阶段:
阶段1: 领导者选择(Leader election)
集合体中每台机器都有一个身份: 要么是领导者(leader), 要么是跟随者(follower).
一个集合体中只有一个领导者, 其余都是跟随者.
集合体中所有机器通过一个选举过程来选出一台机器来作为领导者, 其他的机器就称为了跟随者.
阶段2: 原子广播(Atomic broadcast)
所有的写请求都会被转发给领导者, 再有领导者将更新广播给跟随者.
当半数以上的跟随者已经将修改持久化之后, 领导者才会真正的提交这个更新, 然后客户端才会收到一个更新成功的响应.
这个用来达成共识的协议被设计成具有原子性, 因此每个修改要么成功要么失败.
如果领导者出现故障, 其余的机器会自动再选出另外一个领导者, 并和新的领导者一起继续提供服务. 随后, 如果之前的领导者恢复正常, 则会成为一个跟随者.
领导者的选举过程是非常快的, 只需要大概200毫秒, 因此在领导者选举的过程中不会出现系统性能的明显降低.
任何一台机器都可以为读请求提供服务, 并且由于读请求只涉及内存检索, 因此非常快.
3.5.3 领导者选举过程详解
Leader 选举是保证分布式数据一致性的关键所在。当 Zookeeper 集群中的一台服务器出现以下两种情况之一时,需要进入 Leader 选举:
- 服务器初始化启动。
- 服务器运行期间无法和 Leader 保持连接。
选举机制中的一些概念
服务器ID(myid)
比如有 5 台服务器, 他们的 ID 编号分别是1, 2, 3, 4, 5.
编号越大, 在选举算法中的权重也越大.
数据ID(或者也叫事务ID, zxid)
服务器中存放的最大数据ID.
值越大表示数据越新, 在选举算法中数据越新权重越大. 逻辑时钟
或者叫投票的次数.
同一轮投票过程中的逻辑时钟值是相同的。 每投完一次票这个数据就会增加,然后与接收到的其它服务器返回的投票信息中的数值相比,根据不同的值做出不同的判断。选举状态
- LOOKING : 精选状态(正在找领导)
- FOLLOWING : 随从状态,同步leader状态,参与投票。
- OBSERVING : 观察状态, 同步leader状态,不参与投票。
- LEADING : 领导者状态(我是领导了)
选举消息内容 在投票完成后,需要将投票信息发送给集群中的所有服务器,它包含如下内容。
- 服务器ID
- 数据ID
- 逻辑时钟
- 选举状态
服务器启动时期的 Leader 选举
目前有 5 台服务器,每台服务器均没有数据(可以认为他们的数据id都是0, 所以选领导看每个服务器的id),它们的编号分别是1,2,3,4,5,按编号依次启动,它们的选择举过程如下:
服务器1启动, 给自己投票, 然后广播投票信息(
<1, 0>
格式:<myid, zxid>
). 由于其他服务器都没有启动, 所以收不到任何反馈信息.服务器1的状态一直属于LOOKING
服务器2启动, 给自己投票, 然后广播投票信息(
<2, 0>
)(与其他服务器交换选举信息).由于服务器的2的id大于服务器1的id, 所以服务器2胜出. 由于现在启动的服务器的没有超过半数, 所以服务器2并不能当成为leader
, 仍然是LOOKING
状态. 但是服务器1会更新的自己的投票信息为(<2, 0>
).服务器3启动, 给自己投票(
<3, 0>
), 与其他服务器交换投票信息. 最终的结果是服务器3胜出, 且由于启动的服务器的数量已经超过了半数, 所以服务器3成为leader
, 服务器1和服务器2自动成为follower
服务器4启动, 根据前面的信息, 虽然服务器4的
id
比较大, 但是由于已经产生了领导, 所以服务器4只能是follower
服务器5启动, 命运和服务器4一样.
服务器运行时期的 Leader 选择( Leader 挂了)
在 Zookeeper 运行期间,Leader
与 非Leader
服务器各司其职,即便当有非Leader
服务器宕机或新加入,此时也不会影响Leader
.
但是一旦Leader
服务器挂了,那么整个集群将暂停对外服务(200ms
左右),进入新一轮Leader
选举,其过程和启动时期的Leader
选举过程基本一致。
假设正在运行的有Server1
、Server2
、Server3
、Server4
、Server5
三台服务器,当前Leader
是Server3
,若某一时刻Leader
挂了,此时便开始Leader
选举。选举过程如下:
为了分析方便, 假设仍然没有数据.(如果有数据的话, 数据id优先.)
变更状态. 所有的
follower
都会讲自己的服务器状态变成LOOKING
.开始选举, 每个服务器发送一个投票. 第一轮投票都会选举自己.
<自己的id, 0>
每个服务器都会接收到其他服务器的投票. 与气动过程相同
处理投票
统计投票
选出领导, 更新状态.