zookeeper保持数据一致性是通过Zab协议实现的。
ZAB协议
ZAB全称是Zookeeper Atomic Broadcast(Zookeeper原子广播),ZAB借鉴了Paxos,是为zookeeper专门设计的一种支持崩溃恢复的原子广播协议,是zookeeper保证数据一致性的核心算法。基于该协议,zk实现了一种主备模型的系统架构来保证集群中各个副本之间的数据一致性。
主备模型指一台服务器(Leader)负责处理外部写事务请求,然后Leader客户端将数据同步到其他(Follwer)节点。
消息广播
zk集群中的消息广播模式就是数据副本的传递策略,zk中数据副本的传递方式与二段提交(2PC)类似,二段提交要求协调者必须等到参与者全部反馈ACK确认消息后,再发送commit,Zab中只要Leader等待半数以上的Follower反馈ACK即可。具体步骤如下:
-
Zookeeper客户端发起写请求,Leader会封装成一个事务Proposal提案,同时为每个事务Proposal 分配一个全局事务ID,即zxid。
-
Leader服务器为每个Follower服务器分配一个单独的队列,然后将需要广播的Proposal依次放入队列中,按照FIFO发送。单独的队列可以做到异步解耦,避免同步方式引起阻塞,影响性能。
-
Follower接收到Proposal后,先以事务日志的方式写入本地磁盘,写入成功后向Leader反馈ACK,此时并没有提交事务。
-
Leader接收到半数以上ACK即认为发送成功,并向所有Follower广播commit,同时自身完成事务提交。
-
Follower接收到commit 消息后,会将上一条事务提交。
zxid
在 Zab 的事务编号 zxid 设计中,zxid是一个64位的数字。
其中低32位可以看成一个简单的单增计数器,针对客户端每一个事务请求,Leader 在产生新的 Proposal 事务时,都会对该计数器加1。而高32位则代表了 Leader 周期的 epoch 编号。
epoch 编号可以理解为当前集群所处的年代,或者周期。每次Leader变更之后都会在 epoch 的基础上加1,这样旧的 Leader 崩溃恢复之后,其他Follower 也不会听它的了,因为 Follower 只服从epoch最高的 Leader 命令。
每当选举产生一个新的 Leader ,就会从这个 Leader 服务器上取出本地事务日志中最大编号 Proposal 的 zxid,并从 zxid 中解析得到对应的 epoch 编号,然后再对其加1,之后该编号就作为新的 epoch 值,并将低32位数字归零,由0开始重新生成zxid。
崩溃恢复
一旦Leader服务器出现崩溃或者失去了与半数Follower的联系,那么就会进入崩溃恢复模式。
有两种崩溃假设:
-
事务在Leader上提交了,并且过半Follower响应了ACK,Leader自身完成提交事务和发送了部分commit
-
事务在Leader提交了之后就挂了
针对这些问题,Zab定义了两个原则:
-
确保已经被 Leader 提交的 Proposal 必须最终被所有的 Follower 服务器提交。
-
确保丢弃那些只在 Leader 提出/复制,但没有提交的事务。
所以zk采用了FastLeaderElection选举算法,通过这个算法新选举出来的Leader不包含未提交的 Proposal ,并且新选举出来的Leader节点中包含最大的zxid。
Leader选举
当系统启动或者崩溃恢复后,都会进行Leader选举。
选举过程中会涉及以下关键词:
-
Looking 竞选状态
-
Following 跟随者状态,参与投票
-
Obsering 观察者状态,不参与投票
-
Leading 领导者状态
-
(sever_id,zxid) 服务器产生的投票,sever_id代表服务器的标识id。投票会对比自己的投票和收到的投票,优先比较zxid的大小并根据结果决定是否更新自己的投票,如果相同则比较sid。
集群启动 时步骤如下,假设有3台服务器:
-
服务器启动时每个服务器的状态都是Looking。
-
Server1启动,给自己投票 (1,0),然后发送给集群中所有服务器,由于其他服务器还没有启动,所以Server1仍处于Looking状态。
-
Server2启动,给自己投票(2,0),然后发送给集群中所有服务器,由于Server2的sid比Server1的sid大,所以Server1转投(2,0),此时Server2获得大于半数的两票,成为Leader,集群中服务器变更自己的状态为Following或者是Leading。
-
Server3启动,给自己投票(3,0),然后发送给集群中所有服务器,尽管Server3sid大,但之前Server2已经进入Leading,所以不会改变Leader结果,变更状态为Following。
崩溃恢复后时的Leader选举和系统启动时类似,区别在于只有非observer服务器会进入Looking状态,并且zxid可能不同。
数据同步
完成Leader选举后,此时新的Leader具有最高的zxid,在正式开始工作之前,Leader会确认事务日志中的所有的 Proposal 是否已经被集群中过半的服务器 Commit。紧接着需要确保所有的 Follower 服务器能够接收到每一条事务的 Proposal ,并且能将所有已经提交的事务 Proposal 应用到内存数据中。等到Follower 将所有尚未同步的事务 Proposal 都从 Leader 服务器上同步过并且应用到内存数据中以后,Leader 才会把该 Follower 加入到真正可用的 Follower 列表中。
处理需要丢弃的 Proposal
Zab 协议通过 epoch 编号来区分 Leader 变化周期,能够有效的避免不同Leader错误的使用了相同的zxid 提出了不一样的Proposal 的异常情况。
当一个包含了上一个Leader周期中尚未提交过的Proposal启动时,会以Following的角色连接Leader服务器,Leader服务器会根据自己服务器上最后提交的Proposal和这个Following比对,然后要求Follower进行回退操作,回退的版本为已经被集群中过半服务器commit的最新Proposal。
网友评论