zookeeper选举机制
CAP理论
CAP原则又称CAP定理,指的是在一个分布式系统中,一致性(Consistency)、可用性(Availability)、分区容错性(Partition tolerance)。CAP 原则指的是,这三个要素最多只能同时实现两点,不可能三者兼顾。
- ⼀致性:更新操作成功并返回客户端完成后,所有节点在同⼀时间的数据完全⼀致。
- 可⽤性:可⽤性指“Reads and writes always succeed”,即服务⼀直可⽤,⽽且是正常响应时间(指的是一切访问正常并且打开的值是自己预期的值,这叫可用)。
- 分区容错性:即分布式系统在遇到某节点或⽹络分区故障的时候,仍然能够对外提供满⾜⼀致性或可⽤性的服务。——避免单点故障,就要进⾏冗余部署,冗余部署相当于是服务的分区,这样的分区就具备了容错性。
Zookeeper追求的是CAP当中的CP,虽然追求的是一致性,但是在数据同步时,实际上并不是强⼀致性,⽽是顺序⼀致性(事务id的单调递增)。这一点从上面的主从服务器之间的数据同步就可以看出!
CAP权衡
通过 CAP 理论,我们知道⽆法同时满⾜⼀致性、可⽤性和分区容错性这三个特性,那要舍弃哪个呢?
对于多数⼤型互联⽹应⽤的场景,主机众多、部署分散,⽽且现在的集群规模越来越⼤,所以节点故障、⽹络故障是常态,⽽且要保证服务可⽤性达到 99.9999,即保证 P 和 A,舍弃C(退⽽求其次保证最终⼀致性)。虽然某些地⽅会影响客户体验,但没达到造成⽤户流程的严重程度。
对于涉及到钱财这样不能有⼀丝让步的场景,C 必须保证。⽹络发⽣故障宁可停⽌服务,这是保证 CA,舍弃 P。貌似这⼏年国内银⾏业发⽣了不下 10 起事故,但影响⾯不⼤,报到也不多,⼴⼤群众知道的少。
还有⼀种是保证 CP,舍弃 A。例如⽹络故障是只读不写。
孰优孰略,没有定论,只能根据场景定夺,适合的才是最好的。
CAP原理简单证明
假设有节点data1和节点data2,一开始有个数据number=1,之后向data1提交更新,将数据number设置为2.
接着data1就需要将更新推送给data2,让data2也更新number数据。
于是接下来我们分3个场景分析:
-
在保证CP的情况下:
为了保证数据数据一致性,data1 需要将数据复制给 data2,那么两个节点之间需要建立通信,可由于网络原因,通讯出现了问题,导致失败了,比较网络是不可靠的。不过系统又保证了分区容错性,这时候 data2 就不一定能够及时的收到 data1 推送来的数据同步信息。当客户端有请求来访问 data2 的number数据时,为了保证数据一致性,data2 只能阻塞,或者返回错误,提示系统发生了错误,然后等待数据真正同步完成后再返回。
所以,在保证C 和 P的情况下,是无法同时保证A的
-
在保证AP的情况下:
为了保证系统的高可用性,data1 和 data2 都需要在有限时间内返回。同样的由于网络的不可靠,在有限时间内,data2 有可能还没有收到 data1 发来的数据同步信息,这时候返回给客户端的可能就是旧数据,和访问 data1 的数据是不一致的,并不满足C。
所以,在保证A and P的情况下,是无法同时保证C的。
-
在保证AC的情况下:
如果要保证高可用和一致性,只有在网络情况良好且可靠的情况下才能实现。这样 data1 才能立即将更新的消息发送给 data2。但是我们都知道网络是不可靠的,是会存在丢包的请客。所以要满足及时可靠更新,只有将两个节点data1 data2放到一个区内才可以,也就丧失了P这个保证。其实这时候整个系统也不能算是一个分布式系统了。
**理解CAP理论的最简单方式:**可以想象两个节点分处在分区两侧。允许至少一个节点更新状态会导致数据的不一致,即丧失了C性质。如果为了保证数据一致性,将分区一侧的节点设置为不可用,那么又丧失了A性质。除非两个节点互相通信,才能既保证C又保证A,这又会导致丧失P性质。
选举过程
假设有以下五台服务器组成的 Zookeeper 集群:server1、server2、server3、server4、server5
每个server都有一个自身的myid(zookeeper配置文件),这里按1、2、3、4、5算,在选举过程中主要是依据zxid和myid来进行轮训server,然后比较统计投票
zxid:zookeeper transcaction id,事务id,就是只要在这个服务下做的增删改操作,查不算,每操作一次,那么zxid就会+1,每次请求对应一个唯一的zxid,如果zxid a < zxid b,则可以保证a一定发生在b之前。
选举分为两种情况,初始化和leader挂掉的时候,要进行leader选举,至少需要2台机器,集群机器台数基本是奇数。
初始化
当启动初始化集群时,server1的myid为1,zxid为0,server2的myid为2,zxid同样是0,以此类推。此种情况下zxid都是0。先比较zxid,在比较myid。
服务器1启动,给自己投票,然后发投票信息,由于其他机器还没有启动,所以它收不到反馈信息,服务器1的状态一直属于LOOKING(选举状态)
服务器2启动,给自己投票,同时与之前启动的服务器1交换信息,由于服务器2的myid大所以服务器2胜出,但此时投票数没有大于半数,所以两个服务器的状态依然属于LOOKING(选举状态)
服务器3启动,给自己投票,同时与之前启动的服务器1、2交换信息,由于服务器3的myid大所以服务器3胜出,因此服务器3成为Leader,服务器1、2成为小弟
服务器4启动,给自己投票,同时与之前启动的服务器1、2、3交换信息,尽管服务器4的myid最大,但之前服务器3已经胜出,所以服务器4只能被迫成为小弟
服务器5启动,给自己投票,后面的逻辑同服务器4,被迫成为小弟(当选举机器过半时,已经选出Leader后,后面就跟随已选出的Leader)
运行期间
按照上述初始化的情况,server3成为了Leader,在运行期间如果处于Leader的server3挂了,那么非Observer服务器server1、server2、server4、server5会将自己的节点状态变为LOOKING状态,之后的流程为:
- 开始进行Leader选举。现在选举同样根据myid和zxid来进行
- 首先每个server都会给自己投一票来竞选Leader。假设server1的zxid为123、server2的zxid为124、server4的zxid为169、server5的zxid为188
- 同样先是比较zxid=再比较myid,那么server1、server2、server4比较之后server4根据优先条件当选Leader。然后server5还是跟随server4被迫成为小弟,因为即使server5的zxid最大,但是当选举到server4时,选举机器数已过半,不在选举,则后面的server5只能跟随已经选举的Leader
zookeeper集群为保证数据的一致性所有操作都是由Leader完成,之后再由Leader同步给follower。重点就在这里,zookeeper并不会确保所有的节点都同步完成数据,只要有大多数节点(即n/2+1)同步成功即可。
假设有一个写操作成功,那么现在数据只存在于节点Leader,之后Leader再同步给其他follower。这时候宕掉3个机器,已经过半的机器无法进行投票选举,剩余两台不足过半,无法选举(即无法提供任何服务)。需要再启动一个机器恢复服务。
所以挂掉的机器不要过半,过半就会导致无法正常服务。