1. 算法基础
思考:Zookeeper 是如何保持数据一致性的?
1.1. 拜占庭将军问题
拜占庭将军是一个协议问题,拜占庭帝国军队的将军们必须全体一致地决定是否攻击某一支敌军。问题是这些将军地理上是分隔开来的,并且将军中存在叛徒。叛徒可以任意行动以达到以下目标:欺骗某些将军采取进攻行动;促成一个不是所有所有将军都同意的决定,如当将军们不希望进攻的时候促成进进攻行动;或者迷路某些将军,使他们无法做出决定。如果叛徒达到了这些目的之一,则任何的攻击行动都是注定失败的,只有达到完全也一致的努力才能获得胜利。
1.2. Paxos 算法
1.2.1. Paxos算法解决什么问题?
Paxos 算法:一种基于消息传递且具有高度容错特性的一致性算法。
Paxos 算法解决的问题:就是如何快速正确地在一个分布式系统中对某个数据值达成一致,并且保证无论发生什么异常,都不会破坏整个系统的一致性。
1.2.2. Paxos 算法描述
在一个 Paxos 系统中,首先将所有的节点划分成 Proposer (提议者),Acceptor(接受者),和 Learner(学习者) 。 (注意:每个节点都能身兼数职)。
一个完整的 Paxos 算法流程分为三个节点:
- Prepare 准备阶段
-
- Proposer 向多个 Acceptor 发出 Proposer 请求 Promise(承诺)
- Acceptor 针对收到的 Propose 请求进行分 Promise(承诺)
- Accept 接受阶段
-
- Proposer 收到多数的 Acceptor 承诺的 Promise 后,向 Acceptor 发出 Propose 请求
- Acceptor 针对收到的 Propose 请求进行 Accept 处理
- Learn 学习阶段
-
- Proposer 将形成的决议发送给所有的 Learners
1.2.3. Paxos 算法流程
- Prepare:Proposer 生成全局唯一且递增的 Proposal ID,向所有的 Accpetor 发送 Propose 请求,这里不需要携带提案内容,只携带 Proposal ID 就可以了。
- Promise Acceptor 收到 Propose 请求后,做出“两个承诺,一个应答”。
- 不再接受 Proposal ID 小于等于 (注意:这里是 <=) 当前请求的 Propose 请求。
- 不再接受 Proposal ID 小于 (注意,这里是 < ) 请求的 Accept 请求。
- 不违背以前做出的承诺的前提下,回复已经 Accept 过的提案里面 Proposal ID 中最大的那个提案的 Value 和 Proposal ID,没有的话就返回空值。
- Propose:Proposer 收到多数 Acceptor 的Promise 应答之后,从应答中选择 Proposal ID 最大的提案的 Value,作为本次要发起的提案。如果所有应答提案 Value 都是空值,这可以自己随意决定提案 Value。然后携带当前 Proposal ID,向所有 Acceptor 发送 Propose 请求。
- Accept:Acceptor 收到 Propose 请求后,在不违背自己之前做出的承诺下,接受并持久化当前 Proposal ID 和提案 Value。
- Learn:Proposer 收到多数 Acceptor 的 Accept 后,决议形成,然后将形成的决议发送给所有的 Learner。
1.2.4. 情况分析
情况1 :比较顺利
情况 2:意外情况
情况3 :活锁
造成这种情况的原因在于系统中有一个以上的 Proposer,多个 Proposers 相互争夺 Acceptor,造成迟迟没办法达成一致地情况。针对这种情况,一种改进的 Paxos 算法被提出:从系统中选出一个节点作为 Leader,只有Leader 才能够发起提案。 这样,一次 Paxos 流程中只有一个 Proposer,不会出现活锁的情况,此时只会出现例子中的第一种情况。
1.3. ZAB 协议
1.3.1. 什么是 ZAB 算法
ZAB 借鉴了 Paxos 算法,是特别为 Zookeeper 设计的支持崩溃恢复的原子广播协议。基于该协议,Zookeeper 设计为只有一台客户端(Leader)负责处理外部的写事务请求,然后 Leader 客户端将数据同步到其他 Follower 节点。即 Zookeeper 只有一个Leader 可以发起提案。
1.3.2. ZAB 协议内容
ZAB 协议包括两种基本模式:消息广播、崩溃恢复。
消息广播过程
- 客户端发起一个写操作请求
- Leader 服务器将客户端的请求转化为事务 Proposal 提案,同时为每个 Proposal 分配一个全局的 ID,即 zxid。
- Leader 服务器为每个 Follower 服务器分配一个单独的队列,然后将需要广播的 Proposal 依次放到队列中去,并且根据 FIFO 策略进行消息发送。
- Follower 接收到 Proposal 过后,会首先将事务日志以写入的方式写入到本地磁盘中去,写入成功之后向 Leader 反馈一个 Ack响应消息。
- Leader 接收到超过半数以上的 Follower 的 Ack 响应消息后,即认为消息发送成功,可以发送 Commit 消息。
- Leader 向所有 Follower 广播 Commiy 消息,同时自身也会完成事务的提交。Follower 接收到 commit 消息之后,会将上一条事务提交。
- Zookeeper 采用 ZAB 协议的核心,就是只要有一台服务器提交了 Proposal之后,就要确保所有的服务器最终都可以正确提交 Proposal。
ZAB 协议针对事务请求的处理过程类似于一个两阶段提交过程:
- 广播事务阶段
- 广播提交阶段
这两个阶段提交模型如下,有可能应为 Leader 宕机带来数据不一致,比如:
- Leader 发起一个事务 Proposal1 之后就宕机了, Follower 都没有收到 Poposal1
- Leader 收到半数 ACK 宕机,没来得及向 Follower 发送 Commit
崩溃恢复——异常假设
- 假设两种服务器异常的情况:
-
- 假设一个事务在 Leader 提出来之后,Leader 就挂了
- 一个事务在 Leader 上提交了,并且过半的 Follower 都响应 Ack 了,但是 Leader 在 Commit 消息发出之前挂了
- ZAB 协议崩溃恢复要求满足以下两个要求:
-
- 确保已经被 Leader 提交的提案 Proposal ,必须最终被所有的 Follower 服务器提交。 (已经产生的提案,Follower 必须执行)
- 确保丢弃已经被 Leader 提出的,但是没有被提交的 Proposal。 (丢弃已经胎死腹中的提案)
崩溃恢复——Leader 选举
Leader 选举: 根据上述要求要求,ZAB 协议需要保证选举出来的 Leader 满足以下条件:
- 新选举出来的 Leader 不能包含未提交的 Proposal。即新 Leader 必须都是已经提交了 Proposal 的 Follower 服务器节点
- 新选举的 Leader 节点中包含最大的 zxid, 这样做的的好处是可以避免 Leader 服务器检查 Proposal 的提交和丢弃工作。
崩溃恢复——数据恢复:新Leader 产生之后,如何进行数据同步呢
ZAB 如何实现数据同步
- 完成 Leader 选举后,在正式工作开始之前(接收事务请求,然后提出新的 Proposal),Leader 服务器会首先确认事务日志中的所有的 Proposal 是否已经被集群中过半的服务器 Commit。
- Leader 服务器需要确保所有的 Follower 服务器可以接收到每一条事务的Proposal,并且可以将所有已经提交的事务 Proposal 应用到内存数据中。等到 Follower 将所有尚未同步的事务 Proposal 都从 Leader 服务器上同步过以后,并且应用到内存数据中以后, Leader 才会将该 Follower 加入到真正可用的 Follower 列表中去。
1.4. CAP 理论
CAP 理论告诉我们,一个分布式系统不可能同时满足以下三个基本需求:
- 一致性(C:Consistency)
- 可用性(A:Available)
- 分区容错性(P:Partition Tolerance)
这三个基本需求中,最多只能满足两项,因为 P 是必须的,所以一般都会选择 CP 或者 AP 其中一个理论满足。
- 一致性(C:Consistency)
在分布式环境种类,一致性指的是数据在多个副本之间是否可以保持数据一致的特性。在一致性的需求下,当一个系统在数据一致的状态下执行更新操作后,应该保证系统的数据仍然处于一致的状态。
- 可用性(A:Available)
可用性是指系统提供的额服务必须一致处于可用的状态,对于用户的每一个操作请求总是可以在有限的时间内返回结果。
- 分区容错性(P:Partition Tolerance)
分布式系统在遇到任何网络分区故障的时候,仍然需要能够保证对外提供满足一致性和可用性的服务,除非是整个为网络环境都发生了故障。
Zookeeper 保证的是 CP
- Zookeeper 不能保证每次服务请求的可用性。(注:极端环境下,Zookeeper 可能会丢弃一些请求,消费者程序需要重新请求才能获得结果)。所以说,Zookeeper 不能保证服务可用性。
- 进行 Leader 选举的sh8ihou集群是不可用的,