顺序一致性:
zookeeper专门设计了zab协议作为其数据一致性协议,其原理由leader统一写入,使用两阶段提交,达到数据一致性。
数据写入过程:每次客户端的写入请求会被转发至leader处理,leader会把此写入事件作为提案广播给所有Follower节点,写入成功(顺序写入到日志中)的Follower会向leader返回ack,此为阶段一。然后当leader接受到半数以上的ack时便确认数据可以写入,然后向所有Follower广播commit将提案生效,就是将日志中的数据应用到内存中,即znode中,此时客户端就能访问到写入的数据,此为阶段二。 但有可能有的Follower还没有写入最新的数据,所以它不是强一致性的。
zookeeper保证的时顺序一致性,即每个节点的数据都是严格按照事务的发起顺序来生效的,保证顺序的关键点在于zxid,zxid由leader生成,是递增的,每个follower都应该保存了最近一次zxid,在leader宕机重新选举时,哪个follower的zxid最大,就有机会成为leader,因为zxid最大代表了它的数据最新。zxid结构如下:

ZXID有两部分组成:
任期:完成本次选举后,直到下次选举前,由同一Leader负责协调写入,比如在一个任期内,前半段都是1;事务计数器:单调递增,每生效一次写入,计数器加一。可以看到,ZXID的低32位是计数器,所以同一任期内,ZXID是连续的,每个结点又都保存着自身最新生效的ZXID,通过对比新提案的ZXID与自身最新ZXID是否相差“1”,来保证事务严格按照顺序生效的。
在并发场景下,leader为了保证提案按zxid顺序生效,使用了一个ConcurrentHashMap,名为outstandingProposals,记录所有未提交的提案,key为zxid,value为提案信息,对其访问逻辑如下:
1.发起一个提案,会将提案的zxid和内容放到map中
2.收到follower的ack后,将ack中的zxid从map中找到对应的提案,对ack计数
3.执行tryToCommit,先要判断当前zxid之前是否还有未提交的提案,有就暂时不能提交;判断ack是否到达半数以上,达到才可提交。如果可以提交,将当前zxid从map中清除并向所有follower广播提交当前方案。
由于前提是保证顺序提交的,所以Leader只需判断outstandingProposals(map)里,当前ZXID的前一个ZXID是否存在,代码如下:

网友评论