ZAB思考

前言

ZAB是专门为Zookeeper设计的分布式协调协议,其全称是Zookeeper Atomic Broadcast(Zookeeper原子广播协议) 。

在本篇文章中,我不会特别详细地去讲ZAB的详细内容和步骤,而是会列出一些我觉得很重要的点或者是理解ZAB的脉络,然后从这些点展开来讲。在看文章之前建议先看下本文的参考博客。

脉络

ZAB的大致内容

  1. ZAB的出现是为了解决Zookeeper内部的一致性问题,它主要分为两大部分,崩溃恢复和原子广播。
  2. 与Paxos不同,在ZAB中,每个进程最终是会有主备的关系的,在ZAB内部被称为Leader和Follower。与很多其他的主从架构一样,对于系统的写操作,都是最终会由Leader来统一处理,而对于读操作,Follower可以单独来处理。而正因为这个原因,Zookeeper在一定程度上丧失了强一致性,后面具体解释。

ZAB的效果

  1. 使用单一进程来处理写请求,并把请求以proposal的方式广播到所有的follower上去;
  2. 保证了全局事务的有序性。因为利用单进程来处理写请求,那么就可以在处理写请求的时候为每个请求生成一个独特的id,在后续的广播或者崩溃恢复的过程中利用这个id来保证所有副本上的全局事务的有序性;
  3. 当leader挂掉的时候,可以利用ZAB的崩溃恢复中的选举部分选举新的leader。

ZAB原理

发现,同步,广播

  • 发现:要求zookeeper集群必须选举出一个 Leader 进程,同时 Leader 会维护一个 Follower 可用客户端列表。将来客户端可以和这些 Follower节点进行通信。
  • 同步:Leader 要负责将本身的数据与 Follower 完成同步,做到多副本存储。这样也是提现了CAP中的高可用和分区容错。Follower将队列中未处理完的请求消费完成后,写入本地事务日志中。
  • 广播:Leader 可以接受客户端新的事务Proposal请求,将新的Proposal请求广播给所有的 Follower。

其实用通俗一点的话说,理解ZAB最重要的就是要知道,ZAB是利用单一的leader节点来作为写请求的入口,而且leader和所有的follower互相之间都知道彼此的存在,这样就可以在每个节点内部维护一份全量的节点名单。而leader在获取写请求后,会把请求包装成proposal发送给所有的follower做备份,与2PC不一样的是,只要有超过一半的follower返回了ACK,那么leader就会向所有的follower发送Commit,并且自己也会做commit。

内容

消息广播

ZAB之所以对2PC做出了改进,不需要所有的follower作出ack后才会提出commit,是因为这样可以大大减小阻塞的时间。

在真实的应用中,客户端会随机连接到Zookeeper的任意节点,这样客户端的写请求就可能发送到leader节点或者是follower。但是大致区别是不大的,本质上都是一个2PC(改进版的)过程。

写leader节点:

写follower节点:

根据上图总的来说,写Leader主要有:

  1. 客户端向Leader发起写请求
  2. Leader将写请求以Proposal的形式发给所有Follower并等待ACK
  3. Follower收到Leader的Proposal后返回ACK
  4. Leader得到过半数的ACK(Leader对自己默认有一个ACK)后向所有的Follower和Observer发送Commmit
  5. Leader将处理结果返回给客户端

而写follower只是在写leader的第一步之前加了一步转发给leader而已。

另外值得一提的是,leader服务器与每个follower之间都有一个单独的队列进行收发消息,使用队列消息可以做到异步解耦。leader和follower之间只要往队列中发送了消息即可。如果使用同步方式容易引起阻塞。性能上要下降很多。

崩溃恢复

既然ZK内部发生了崩溃,那么是如何恢复数据,保证数据的一致性呢?在广播中我们可以知道是通过类似2PC的过程来做的,但是既然ZK内部Leader崩溃了,那么便无法像原子广播一样直接采取这种方式来做。在Leader崩溃后,一般来说会有两种情况:

  1. leader在提出proposal时未提交之前崩溃,则经过崩溃恢复之后,新选举的leader一定不能是刚才的leader。因为这个leader存在未提交的proposal。
  2. leader在发送commit消息之后,崩溃。即消息已经发送到队列中。经过崩溃恢复之后,参与选举的follower服务器(刚才崩溃的leader有可能已经恢复运行,也属于follower节点范畴)中有的节点已经是消费了队列中所有的commit消息。即该follower节点将会被选举为最新的leader。剩下动作就是数据同步过程。

其实总的来说就是一种是在commit前,一种在commit后,区别核心在于follower是否有机会执行commit。

为了解决这个问题,ZAB提出了崩溃恢复的两个要求:

  1. 确保leader提交的proposal必须被所有的follower服务器提交;
  2. 确保丢弃已经被leader提出的但没有提交的proposal。

为了解决这个问题,ZAB采用的策略是重新进行选举,然后利用特殊的机制同步新的leader和follower之间的数据,这样保证新的leader和follower之间的数据一致性。总的来说就是三个阶段:1. 选举; 2. 恢复; 3. 广播。

选举

ZAB利用了特殊的被称为Fast Leader Election的选举策略。

简单的来说,在Zookeeper内部的zxid是一个64位的变量,其中前32位被称为epoch或者logicalClock,它的含义是代表这是该服务器发起的第多少轮投票;而低32位则代表这是当前epoch下第多少次的写请求。在参考1和3中有详细的分析过程。

选举的流程大致如下:

  1. follower发现leader崩溃,这时就会把自己的epoch+1;
  2. 初始化投票(投给自己)
  3. 接受外部投票,投票更新规则如下:
    1. 判断选举轮次(epoch)
    2. 若 epoch 相等,选 zxid 最大的
    3. 若epoch和zxid都一样,那么选择serverid最大的(myid最大)
  4. 统计选票,如果有过半选择了自己,那么自己成为leader
  5. 更新状态。Looking===>Leading/Following

具体的过程可以看参考中的第1,2篇。

同步

选举完后的leader只是准leader,只有当准leader和follower完成了数据的同步后才能成为真正的leader。

之前说过ZAB对崩溃恢复有两个要求,在同步过程中为了满足这两个要求做了下面的操作:

  1. 首先leader会和follower交换最大的commit的zxid,如果发现follower有部分zxid没有commit,那么会把commit这些事务的任务放入各个follower的队列中,这样保证了所有leader的commit都提交了;
  2. 如果follower的zxid有大于leader的zxid的,那么leader会发送trunc命令让follower删除这些zxid对应的事务。这个就保证了未被commit的消息会被丢弃。
广播

这个过程和之前的原子广播一致。

思考与问题

  1. ZAB和Paxos

zookeeper之所以没有采用Paxos,是因为Paxos无法保证全局有序,并且因为在Paxos中,第一阶段会一直竞争acceptor的promise,这个阶段很容易发生活锁,造成效率低下。因此zookeeper引入了leader的概念,并用leader的选举,恢复等(即崩溃恢复)保证系统的高可用。在崩溃恢复的过程中,leader的选举、同步与paxos有一定程度的相似。

  1. Zookeeper是不保证读一致性的

如文章开头部分所说,因为在消息的原子广播过程中,只要得到了大部分的follower的ack就会让follower做commit。而客户端是可以任意连接zookeeper集群的任意节点,如果客户端连接到的节点还没有给leader返回ack,但是大部分的节点都给leader返回了ack并做了commit,那么这时候客户端读取的便不是最新的状态。可以参考4了解详细分析。

  1. zookeeper内部follower是怎么知道leader挂了的?

有几点还没有完全理解,如果一个follower和其他所有follower和leader都失去了联系,它会做什么操作?或者说zookeeper在什么情况下知道leader挂了,特别是follower没办法和其他所有follower联系的情况下,比如集群分裂成两部分,少的那部分会怎么做?在什么时候把following状态改成looking状态?

参考

  1. https://blog.csdn.net/a724888/article/details/80757503

  2. https://www.jianshu.com/p/2bceacd60b8a

  3. https://mp.weixin.qq.com/s?__biz=MzI4NTA1MDEwNg==&mid=2650764806&idx=1&sn=48d6d8a99bbaee3dcac2e12e3bf04ea7&chksm=f3f9c393c48e4a85dd5e4b2f741934ec3919c7fc4d4b7cfc26422f6f0977fbcafb8b30dcec5a&mpshare=1&scene=24&srcid=0105FEZLQd1wIshoJGfSDSX4&key=e58c60872eb97b11b762dca1ca1a39524e285b7e7e284fa90d835cc587cd24be77863773de3eb26f4f361ce6d2cc7102954c85abb312df1ae20260f353cfbffe6df7e7547f7d769809a9156ad4122cb5&ascene=0&uin=Nzc5NTUyMTQw&devicetype=iMac14%252C1+OSX+OSX+10.12.5+build(16F73)&version=12020610&nettype=WIFI&lang=zh_CN&fontScale=100&pass_ticket=S3JxgA9EegL3%252FLyZHJR7A4UUD6efECHmVViRoiiCAEvrQ%252FsOgKfJU7SiM8TTfYn3

  4. http://www.crazyant.net/2120.html

  5. https://www.cnblogs.com/Desneo/p/7608441.html

posted @ 2019-04-18 23:16  SmallMushroom  阅读(428)  评论(0编辑  收藏  举报