当前位置:首页 » 操作系统 » zookeeper的paxos算法

zookeeper的paxos算法

发布时间: 2023-10-19 21:09:30

Ⅰ Raft 算法(详细版)

在分布式系统中,一致性算法至关重要。在所有一致性算法中,Paxos 最负盛名,它由莱斯利·兰伯特(Leslie Lamport)于 1990 年提出,是一种基于消息传递的一致性算法,被认为是类似算法中最有效的。

Paxos 算法虽然很有效,但复杂的原理使它实现起来非常困难,截止目前,实现 Paxos 算法的开源软件很少,比较出名的有 Chubby、LibPaxos。此外,Zookeeper 采用的 ZAB(Zookeeper Atomic Broadcast)协议也是基于 Paxos 算法实现的,不过 ZAB 对 Paxos 进行了很多改进与优化,两者的设计目标也存在差异——ZAB 协议主要用于构建一个高可用的分布式数据主备系统,而 Paxos 算法则是用于构建一个分布式的一致性状态机系统。

由于 Paxos 算法过于复杂、实现困难,极大地制约了其应用,而分布式系统领域又亟需一种高效而易于实现的分布式一致性算法,在此背景下,Raft 算法应运而生。

Raft 算法在斯坦福 Diego Ongaro 和 John Ousterhout 于 2013 年发表的《In Search of an Understandable Consensus Algorithm》中提出。相较于 Paxos,Raft 通过逻辑分离使其更容易理解和实现,目前,已经有十多种语言的 Raft 算法实现框架,较为出名的有 etcd、Consul 。

根据官方文档解释,一个 Raft 集群包含若干节点,Raft 把这些节点分为三种状态:Leader、 Follower、Candidate,每种状态负责的任务也是不一样的。正常情况下,集群中的节点只存在 Leader 与 Follower 两种状态。

Leader(领导者) :负责日志的同步管理,处理来自客户端的请求,与Follower保持heartBeat的联系;

Follower(追随者) :响应 Leader 的日志同步请求,响应Candidate的邀票请求,以及把客户端请求到Follower的事务转发(重定向)给Leader;

Candidate(候选者) :负责选举投票,集群刚启动或者Leader宕机时,状态为Follower的节点将转为Candidate并发起选举,选举胜出(获得超过半数节点的投票)后,从Candidate转为Leader状态。

通常,Raft 集群中只有一个 Leader,其它节点都是 Follower。Follower 都是被动的,不会发送任何请求,只是简单地响应来自 Leader 或者 Candidate 的请求。Leader 负责处理所有的客户端请求(如果一个客户端和 Follower 联系,那么 Follower 会把请求重定向给 Leader)。

为简化逻辑和实现,Raft 将一致性问题分解成了三个相对独立的子问题。

选举(Leader Election) :当 Leader 宕机或者集群初创时,一个新的 Leader 需要被选举出来;

日志复制(Log Replication) :Leader 接收来自客户端的请求并将其以日志条目的形式复制到集群中的其它节点,并且强制要求其它节点的日志和自己保持一致;

安全性(Safety) :如果有任何的服务器节点已经应用了一个确定的日志条目到它的状态机中,那么其它服务器节点不能在同一个日志索引位置应用一个不同的指令。

根据 Raft 协议,一个应用 Raft 协议的集群在刚启动时,所有节点的状态都是 Follower。由于没有 Leader,Followers 无法与 Leader 保持心跳(Heart Beat),因此,Followers 会认为 Leader 已经下线,进而转为 Candidate 状态。然后,Candidate 将向集群中其它节点请求投票,同意自己升级为 Leader。如果 Candidate 收到超过半数节点的投票(N/2 + 1),它将获胜成为 Leader。

第一阶段:所有节点都是 Follower。

上面提到,一个应用 Raft 协议的集群在刚启动(或 Leader 宕机)时,所有节点的状态都是 Follower,初始 Term(任期)为 0。同时启动选举定时器,每个节点的选举定时器超时时间都在 100~500 毫秒之间且并不一致(避免同时发起选举)。

第二阶段:Follower 转为 Candidate 并发起投票。

没有 Leader,Followers 无法与 Leader 保持心跳(Heart Beat),节点启动后在一个选举定时器周期内未收到心跳和投票请求,则状态转为候选者 Candidate 状态,且 Term 自增,并向集群中所有节点发送投票请求并且重置选举定时器。

注意,由于每个节点的选举定时器超时时间都在 100-500 毫秒之间,且彼此不一样,以避免所有 Follower 同时转为 Candidate 并同时发起投票请求。换言之,最先转为 Candidate 并发起投票请求的节点将具有成为 Leader 的“先发优势”。

第三阶段:投票策略。

节点收到投票请求后会根据以下情况决定是否接受投票请求(每个 follower 刚成为 Candidate 的时候会将票投给自己):

请求节点的 Term 大于自己的 Term,且自己尚未投票给其它节点,则接受请求,把票投给它;

请求节点的 Term 小于自己的 Term,且自己尚未投票,则拒绝请求,将票投给自己。

第四阶段:Candidate 转为 Leader。

一轮选举过后,正常情况下,会有一个 Candidate 收到超过半数节点(N/2 + 1)的投票,它将胜出并升级为 Leader。然后定时发送心跳给其它的节点,其它节点会转为 Follower 并与 Leader 保持同步,到此,本轮选举结束。

注意:有可能一轮选举中,没有 Candidate 收到超过半数节点投票,那么将进行下一轮选举。

在一个 Raft 集群中,只有 Leader 节点能够处理客户端的请求(如果客户端的请求发到了 Follower,Follower 将会把请求重定向到 Leader) ,客户端的每一个请求都包含一条被复制状态机执行的指令。Leader 把这条指令作为一条新的日志条目(Entry)附加到日志中去,然后并行得将附加条目发送给 Followers,让它们复制这条日志条目。

当这条日志条目被 Followers 安全复制,Leader 会将这条日志条目应用到它的状态机中,然后把执行的结果返回给客户端。如果 Follower 崩溃或者运行缓慢,再或者网络丢包,Leader 会不断得重复尝试附加日志条目(尽管已经回复了客户端)直到所有的 Follower 都最终存储了所有的日志条目,确保强一致性。

第一阶段:客户端请求提交到 Leader。

如下图所示,Leader 收到客户端的请求,比如存储数据 5。Leader 在收到请求后,会将它作为日志条目(Entry)写入本地日志中。需要注意的是,此时该 Entry 的状态是未提交(Uncommitted),Leader 并不会更新本地数据,因此它是不可读的。

第二阶段:Leader 将 Entry 发送到其它 Follower

Leader 与 Followers 之间保持着心跳联系,随心跳 Leader 将追加的 Entry(AppendEntries)并行地发送给其它的 Follower,并让它们复制这条日志条目,这一过程称为复制(Replicate)。

有几点需要注意:

1. 为什么 Leader 向 Follower 发送的 Entry 是 AppendEntries 呢?

因为 Leader 与 Follower 的心跳是周期性的,而一个周期间 Leader 可能接收到多条客户端的请求,因此,随心跳向 Followers 发送的大概率是多个 Entry,即 AppendEntries。当然,在本例中,我们假设只有一条请求,自然也就是一个Entry了。

2. Leader 向 Followers 发送的不仅仅是追加的 Entry(AppendEntries)。

在发送追加日志条目的时候,Leader 会把新的日志条目紧接着之前条目的索引位置(prevLogIndex), Leader 任期号(Term)也包含在其中。如果 Follower 在它的日志中找不到包含相同索引位置和任期号的条目,那么它就会拒绝接收新的日志条目,因为出现这种情况说明 Follower 和 Leader 不一致。

3. 如何解决 Leader 与 Follower 不一致的问题?

在正常情况下,Leader 和 Follower 的日志保持一致,所以追加日志的一致性检查从来不会失败。然而,Leader 和 Follower 一系列崩溃的情况会使它们的日志处于不一致状态。Follower可能会丢失一些在新的 Leader 中有的日志条目,它也可能拥有一些 Leader 没有的日志条目,或者两者都发生。丢失或者多出日志条目可能会持续多个任期。

要使 Follower 的日志与 Leader 恢复一致,Leader 必须找到最后两者达成一致的地方(说白了就是回溯,找到两者最近的一致点),然后删除从那个点之后的所有日志条目,发送自己的日志给 Follower。所有的这些操作都在进行附加日志的一致性检查时完成。

Leader 为每一个 Follower 维护一个 nextIndex,它表示下一个需要发送给 Follower 的日志条目的索引地址。当一个 Leader 刚获得权力的时候,它初始化所有的 nextIndex 值,为自己的最后一条日志的 index 加 1。如果一个 Follower 的日志和 Leader 不一致,那么在下一次附加日志时一致性检查就会失败。在被 Follower 拒绝之后,Leader 就会减小该 Follower 对应的 nextIndex 值并进行重试。最终 nextIndex 会在某个位置使得 Leader 和 Follower 的日志达成一致。当这种情况发生,附加日志就会成功,这时就会把 Follower 冲突的日志条目全部删除并且加上 Leader 的日志。一旦附加日志成功,那么 Follower 的日志就会和 Leader 保持一致,并且在接下来的任期继续保持一致。

第三阶段:Leader 等待 Followers 回应。

Followers 接收到 Leader 发来的复制请求后,有两种可能的回应:

写入本地日志中,返回 Success;

一致性检查失败,拒绝写入,返回 False,原因和解决办法上面已做了详细说明。

需要注意的是,此时该 Entry 的状态也是未提交(Uncommitted)。完成上述步骤后,Followers 会向 Leader 发出 Success 的回应,当 Leader 收到大多数 Followers 的回应后,会将第一阶段写入的 Entry 标记为提交状态(Committed),并把这条日志条目应用到它的状态机中。

第四阶段:Leader 回应客户端。

完成前三个阶段后,Leader会向客户端回应 OK,表示写操作成功。

第五阶段,Leader 通知 Followers Entry 已提交

Leader 回应客户端后,将随着下一个心跳通知 Followers,Followers 收到通知后也会将 Entry 标记为提交状态。至此,Raft 集群超过半数节点已经达到一致状态,可以确保强一致性。

需要注意的是,由于网络、性能、故障等各种原因导致“反应慢”、“不一致”等问题的节点,最终也会与 Leader 达成一致。

前面描述了 Raft 算法是如何选举 Leader 和复制日志的。然而,到目前为止描述的机制并不能充分地保证每一个状态机会按照相同的顺序执行相同的指令。例如,一个 Follower 可能处于不可用状态,同时 Leader 已经提交了若干的日志条目;然后这个 Follower 恢复(尚未与 Leader 达成一致)而 Leader 故障;如果该 Follower 被选举为 Leader 并且覆盖这些日志条目,就会出现问题,即不同的状态机执行不同的指令序列。

鉴于此,在 Leader 选举的时候需增加一些限制来完善 Raft 算法。这些限制可保证任何的 Leader 对于给定的任期号(Term),都拥有之前任期的所有被提交的日志条目(所谓 Leader 的完整特性)。关于这一选举时的限制,下文将详细说明。

在所有基于 Leader 机制的一致性算法中,Leader 都必须存储所有已经提交的日志条目。为了保障这一点,Raft 使用了一种简单而有效的方法,以保证所有之前的任期号中已经提交的日志条目在选举的时候都会出现在新的 Leader 中。换言之,日志条目的传送是单向的,只从 Leader 传给 Follower,并且 Leader 从不会覆盖自身本地日志中已经存在的条目。

Raft 使用投票的方式来阻止一个 Candidate 赢得选举,除非这个 Candidate 包含了所有已经提交的日志条目。Candidate 为了赢得选举必须联系集群中的大部分节点。这意味着每一个已经提交的日志条目肯定存在于至少一个服务器节点上。如果 Candidate 的日志至少和大多数的服务器节点一样新(这个新的定义会在下面讨论),那么它一定持有了所有已经提交的日志条目(多数派的思想)。投票请求的限制中请求中包含了 Candidate 的日志信息,然后投票人会拒绝那些日志没有自己新的投票请求。

Raft 通过比较两份日志中最后一条日志条目的索引值和任期号,确定谁的日志比较新。如果两份日志最后条目的任期号不同,那么任期号大的日志更加新。如果两份日志最后的条目任期号相同,那么日志比较长的那个就更加新。

如同 4.1 节介绍的那样,Leader 知道一条当前任期内的日志记录是可以被提交的,只要它被复制到了大多数的 Follower 上(多数派的思想)。如果一个 Leader 在提交日志条目之前崩溃了,继任的 Leader 会继续尝试复制这条日志记录。然而,一个 Leader 并不能断定被保存到大多数 Follower 上的一个之前任期里的日志条目 就一定已经提交了。这很明显,从日志复制的过程可以看出。

鉴于上述情况,Raft 算法不会通过计算副本数目的方式去提交一个之前任期内的日志条目。只有 Leader 当前任期里的日志条目通过计算副本数目可以被提交;一旦当前任期的日志条目以这种方式被提交,那么由于日志匹配特性,之前的日志条目也都会被间接的提交。在某些情况下,Leader 可以安全地知道一个老的日志条目是否已经被提交(只需判断该条目是否存储到所有节点上),但是 Raft 为了简化问题使用了一种更加保守的方法。

当 Leader 复制之前任期里的日志时,Raft 会为所有日志保留原始的任期号,这在提交规则上产生了额外的复杂性。但是,这种策略更加容易辨别出日志,即使随着时间和日志的变化,日志仍维护着同一个任期编号。此外,该策略使得新 Leader 只需要发送较少日志条目。

raft 的读写都在 leader 节点中进行,它保证了读的都是最新的值,它是符合强一致性的(线性一致性),raft 除了这个还在【客户端交互】那块也做了一些保证,详情可以参考论文。但是 zookeeper 不同,zookeeper 写在 leader,读可以在 follower 进行,可能会读到了旧值,它不符合强一致性(只考虑写一致性,不考虑读一致性),但是 zookeeper 去 follower 读可以有效提升读取的效率。

对比于 zab、raft,我们发现他们选举、setData 都是需要过半机制才行,所以他们针对网络分区的处理方法都是一样的。

一个集群的节点经过网络分区后,如一共有 A、B、C、D、E 5个节点,如果 A 是 leader,网络分区为 A、B、C 和 D、E,在A、B、C分区还是能正常提供服务的,而在 D、E 分区因为不能得到大多数成员确认(虽然分区了,但是因为配置的原因他们还是能知道所有的成员数量,比如 zk 集群启动前需要配置所有成员地址,raft 也一样),是不能进行选举的,所以保证只会有一个 leader。

如果分区为 A、B 和 C、D、E ,A、B 分区虽然 A 还是 leader,但是却不能提供事务服务(setData),C、D、E 分区能重新选出 leader,还是能正常向外提供服务。

1)我们所说的日志(log)与状态机(state machine)不是一回事,日志指还没有提交到状态机中的数据。
2)新 leader 永远不会通过计算副本数量提交旧日志,他只能复制旧日志都其他 follower 上,对于旧日志的提交,只能是新 leader 接收新的写请求写新日志,顺带着把旧日志提交了。

Ⅱ 常见分布式集群选举机制总结

1,Zookeeper -- paxos

2,kafka -- zookeeper上创建节点

3,redis -- 哨兵模式

4,Eureka -- 相互复制

我们探讨这几个集群的选举机制,其实就是探讨它们的高可用性。如果集群中的某些节点挂了,如何保证可用性?这个问题是分布式系统面临的三大问题之一。

Zookeeper的leader选举机制,是这四种集群中最复杂的选举机制,同时也是这四种集群中最接近paxos算法的实现。相比于Zookeeper的选举机制,kafka集群、redis集群、Eureka集群的选举机制简单了许多。

Zookeeper的leader选举是Zookeeper实现数据一致性的关键,同时也存在一些问题。认清Zookeeper的优点与缺陷,对于我们使用好它还是很有必要的。

Zookeeper的选举机制有2个触发条件:集群启动阶段和集群运行阶段leader挂机。这2种场景下选举的流程基本一致,我们以集群运行阶段leader挂机为例来进行说明。leader挂机以后,重新选举leader,选举的流程如下:

1,Zookeeper集群中的follower检测到leader挂机,然后把自己的状态置为LOOKING,开始进行leader选举。

2,每台服务器选举自己为leader,然后把自己的选票通过广播通知其他服务器。

3,每台服务器接收来自其他服务器的选票,并进行合法性校验,主要有两点校验,选举轮次校验和服务器的状态的校验。

4,处理选票。每台服务器都会将自己的选票与其他服务器的选票进行PK,PK的规则如下:

第一个规则:首先进行ZXID的PK,大者获胜。

第二条规则:如果ZXID相等,则进行myid的PK,大者获胜。

经过PK以后,如果当前服务器PK失败,则会把自己的选票重新投给胜者,然后把更新后的选票通过广播通知其他服务器。

5,统计选票。根据超过半数的原则,每台服务器都会统计leader的选票,如果超过半数,则结束选举。

6,更新服务器状态。follower把自己的状态更新为FOLLOWING,leader把自己的状态更新为LEADING。

OK,这就是Zookeeper的leader选举机制。经过若干轮选举以后,Zookeeper集群继续对外提供服务。由于选票PK首先比较的是ZXID,所以Zookeeper能够保证leader的数据是最新的。

kafka集群是如何保证高可用性的呢?

kafka通过Zookeeper管理集群配置、选举leader、consumer group发生变化时进行rebalance。

那么我要问了,kafka是如何选举leader的呢?

概括来说,Kafka选举leader的过程是这样的:kafka的所有broker,在Zookeeper的/controller路径下创建临时节点,成功创建的那个broker就会成为leader,其他的broker就会成为follower。

当leader挂机时,临时节点会被删除,这是其他节点通过Zookeeper的watch机制,会监听到leader的变化,然后所有的follower会再次进行leader选举。

kafka的选举其实就是创建临时节点,这和Zookeeper分布式锁的实现原理基本相同。

redis主从切换和redis集群的理解。

要注意,主从切换默认只有一个master,但是对于多个master的集群,没有主从切换的说法。

redis没有类似Zookeeper的选举机制。redis的master挂掉以后,redis集群是通过主从切换来保证高可用性的。

redis主从切换有2种方式:手动切换和自动切换。

这里我们讨论自动切换,redis主从自动切换需要哨兵模式的支持,哨兵模式简单来说就是:监控master和slave,在master出现故障的时候,自动将slave切换成master,master恢复以后,作为新master的slave对外提供服务。

准确的来说,Eureka集群中的各节点之间不存在主从关系。Eureka集群中的节点的关系是对等的,其他3种集群则都存在主从关系,这是Eureka集群的一个特色。

Eureka集群的各个server之间通过相互注册的方式来实现集群的高可用性。数据同步的方式是增量备份,这样可以保证每个server都是最新最全的数据。从而保证集群的高可用性。这样即使某个server挂了,集群还可以对外提供服务。

Eureka有一个配置项:eureka.client.fetch-register,是否从Eureka server获取注册信息。如果我们是Eureka集群,那么该项配置为true。这样Eureka server直接就可以相互注册。

OK,这篇文章只是对4种集群的选举机制进行了一个概括性的介绍,具体细节还是很复杂的。之前有文章重点分析过Zookeeper的leader选举,后续还会另起文章分析其他几种集群的选举机制,到时候我们再进行更深入的讲解。

Ⅲ Zookeeper 理论基础

ZooKeeper 由雅虎研究院开发,后来捐赠给了 Apache。ZooKeeper 是一个开源的分布式应用程序协调服务器,其为分布式系统提供一致性服务。其一致性是通过基于 Paxos 算法的ZAB 协议完成的。其主要功能包括:配置维护、域名服务、分布式同步、集群管理等。
zookeeper 的官网: http://zookeeper.apache.org

zk 是如何保证分布式系统的一致性的呢?是因为 zk 具有以下几方面的特点:

对于zk 理论的学习,最重要也是最难的知识点就是 Paxos 算法。所以我们首先学习 Paxos算法。

Paxos 算法是莱斯利·兰伯特(Leslie Lamport)1990 年提出的一种基于消息传递的、具有高容错性的一致性算法。Google Chubby 的作者 Mike Burrows 说过,世上只有一种一致性算法, 那就是 Paxos,所有其他一致性算法都是 Paxos 算法的不完整版。Paxos 算法是一种公认的晦涩难懂的算法,并且工程实现上也具有很大难度。较有名的 Paxos 工程实现有Google Chubby、ZAB、微信的 PhxPaxos 等。
Paxos 算法是用于解决什么问题的呢?Paxos 算法要解决的问题是,在分布式系统中如何就某个决议达成一致。

拜占庭将军问题是由 Paxos 算法作者莱斯利·兰伯特提出的点对点通信中的基本问题。该问题要说明的含义是,在不可靠信道上试图通过消息传递的方式达到一致性是不可能的。所以,Paxos 算法的前提是不存在拜占庭将军问题,即信道是安全的、可靠的,集群节点间传递的消息是不会被篡改的。

一般情况下,分布式系统中各个节点间采用两种通讯模型:共享内存(Shared Memory)、消息传递(Messages Passing)。而 Paxos 是基于消息传递通讯模型的。

在 Paxos 算法中有三种角色,分别具有三种不同的行为。但很多时候,一个进程可能同时充当着多种角色。

Paxos 算法的一致性主要体现在以下几点:

Paxos 对于提案的提交算法有两种方案,2PC 与 3PC。

它们的区别主要就在于 accept 阶段中是否包含 commit 功能。具体看下面的描述。

Paxos 算法的 3PC 执行过程划分为三个阶段:准备阶段 prepare、接受阶段 accept,与提交阶段 commit。

若提案者接收到的反馈数量超过了半数,则其会向外广播两类信息:

2PC 与 3PC 的区别是,在提案者接收到超过半数的表决者对于 parepare 阶段的反馈后,其会向所有表决者发送真正的提案 proposal。当表决者接受到 proposal 后就直接将其同步到了本地,不用再等待 commit 消息了。

那么,为什么不直接使用 2PC,而要使用 3PC 呢?是因为 2PC 中存在着较多的弊端(这里就不再展开来说了)。所以很多 Paxos 工业实现使用的都是 3PC 提交。但 2PC 提交的效率要高于 3PC 提交,所以在保证不出问题的情况下,是可以使用 2PC 提交的。

前面所述的Paxos 算法在实际工程应用过程中,根据不同的实际需求存在诸多不便之处, 所以也就出现了很多对于基本 Paxos 算法的优化算法,以对 Paxos 算法进行改进,例如,Multi Paxos、Fast Paxos、EPaxos。

例如,Paxos 算法存在“活锁问题”,Fast Paxos 算法对 Paxos 算法进行了改进:只允许一个进程提交提案,即该进程具有对 N 的唯一操作权。该方式解决了“活锁”问题。

ZAB ,Zookeeper Atomic Broadcast,zk 原子消息广播协议,是专为 ZooKeeper 设计的一种支持崩溃恢复的原子广播协议,在 Zookeeper 中,主要依赖 ZAB 协议来实现分布式数据一致性。

Zookeeper 使用一个单一主进程来接收并处理客户端的所有事务请求,即写请求。当服务器数据的状态发生变更后,集群采用 ZAB 原子广播协议,以事务提案 Proposal 的形式广播到所有的副本进程上。ZAB 协议能够保证一个全局的变更序列,即可以为每一个事务分配一个全局的递增编号 xid。

当 Zookeeper 客户端连接到 Zookeeper 集群的一个节点后,若客户端提交的是读请求, 那么当前节点就直接根据自己保存的数据对其进行响应;如果是写请求且当前节点不是Leader,那么节点就会将该写请求转发给 Leader,Leader 会以提案的方式广播该写操作,只要有超过半数节点同意该写操作,则该写操作请求就会被提交。然后 Leader 会再次广播给所有订阅者,即 Learner,通知它们同步数据。

ZAB 协议是 Paxos 算法的一种工业实现算法。但两者的设计目标不太一样。ZAB 协议主要用于构建一个高可用的分布式数据主从系统,即 Follower 是 Leader 的从机,Leader 挂了, 马上就可以选举出一个新的 Leader,但平时它们都对外提供服务。而 Fast Paxos 算法则是用于构建一个分布式一致性状态机系统,确保系统中各个节点的状态都是一致的。

另外,ZAB 还使用 Google 的 Chubby 算法作为分布式锁的实现,而 Google 的 Chubby 也是 Paxos 算法的应用。

zk 集群对于事务请求的处理是 Fast Paxos 算法的体现,即只允许 Leader 提出提案。其属于 3PC 提交。

但 Leader 选举是 Paxos 算法的体现,因为 Leader 宕机后,所有 Follower 均可提交提案, 它们在最初都是“我选我”。其属于 2PC 提交。

为了避免 Zookeeper 的单点问题,zk 也是以集群的形式出现的。zk 集群中的角色主要有以下三类:

Learner:学习者,同步者。
Learner = Follower + Observer
QuorumPeer = Participant = Leader + Follower

在 ZAB 中有三个很重要的数据:

ZAB 协议中对zkServer 的状态描述有三种模式。这三种模式并没有十分明显的界线,它们相互交织在一起。

zk 集群中的每一台主机,在不同的阶段会处于不同的状态。每一台主机具有四种状态。

在集群启动过程中,或 Leader 宕机后,集群就进入了恢复模式。恢复模式中最重要的阶段就是 Leader 选举。

A、serverId
这是zk 集群中服务器的唯一标识,也称为 sid,其实质就是 zk 中配置的 myid。例如, 有三个 zk 服务器,那么编号分别是 1,2,3。
B、 逻辑时钟
逻辑时钟,Logicalclock,是一个整型数,该概念在选举时称为 logicalclock,而在选举结束后称为epoch。即 epoch 与 logicalclock 是同一个值,在不同情况下的不同名称。

在集群启动过程中的 Leader 选举过程(算法)与 Leader 断连后的 Leader 选举过程稍微有一些区别,基本相同。

A、集群启动中的 Leader 选举

对于 Server1 而言,它的投票是(1, 0),接收 Server2 的投票为(2, 0)。其首先会比较两者的 ZXID,均为 0,再比较 myid,此时 Server2 的 myid 最大,于是 Server1 更新自己的投票为(2, 0),然后重新投票。对于 Server2 而言,其无须更新自己的投票,只是再次向集群中所有主机发出上一次投票信息即可。
(4) 统计投票。每次投票后,服务器都会统计投票信息,判断是否已经有过半机器接受到相同的投票信息。对于 Server1、Server2 而言,都统计出集群中已经有两台主机接受了(2, 0)的投票信息,此时便认为已经选出了新的 Leader,即 Server2。
(5) 改变服务器状态。一旦确定了 Leader,每个服务器就会更新自己的状态,如果是Follower,那么就变更为 FOLLOWING,如果是 Leader,就变更为 LEADING。
(6) 添加主机。在新的 Leader 选举出来后 Server3 启动,其想发出新一轮的选举。但由于当前集群中各个主机的状态并不是 LOOKING,而是各司其职的正常服务,所以其只能是以Follower 的身份加入到集群中。

B、 宕机后的 Leader 选举
在 Zookeeper 运行期间,Leader 与非 Leader 服务器各司其职,即便当有非 Leader 服务器宕机或新加入时也不会影响 Leader。但是若 Leader 服务器挂了,那么整个集群将暂停对外服务,进入新一轮的 Leader 选举,其过程和启动时期的 Leader 选举过程基本一致。

前面我们说过,恢复模式具有两个阶段:Leader 选举与初始化同步。当完成 Leader 选举后,此时的 Leader 还是一个准 Leader,其要经过初始化同步后才能变为真正的 Leader。

具体过程如下:

当集群中的 Learner 完成了初始化状态同步,那么整个 zk 集群就进入到了正常工作模式了。
如果集群中的 Learner 节点收到客户端的事务请求,那么这些 Learner 会将请求转发给Leader 服务器。然后再执行如下的具体过程:

Observer 数量并不是越多越好,一般与 Follower 数量相同。因为 Observer 数量的增多虽不会增加事务操作压力,但其需要从 Leader 同步数据,Observer 同步数据的时间是小于等于 Follower 同步数据的时间的。当 Follower 同步数据完成,Leader 的 Observer 列表中的Observer 主机将结束同步。那些完成同步的 Observer 将会进入到另一个对外提供服务的列表。那么,那些没有同步了数据无法提供服务的 Observer 主机就形成了资源浪费。
所以,对于事务操作发生频繁的系统,不建议使用过多的 Observer。

Leader 中保存的 Observer 列表其实有两个:
all:包含所有 Observer。
service:已经完成了从 Leader 同步数据的任务。service <= all。其是动态的。

Leader 中保存的 Follower 列表其实也有两个:
all:要求其中必须有过半的 Follower 向Leader 反馈ACK
service:

当集群正在启动过程中,或 Leader 崩溃后,集群就进入了恢复模式。对于要恢复的数据状态需要遵循三个原则。

若集群中 Leader 收到的 Follower 心跳数量没有过半,此时 Leader 会自认为自己与集群的连接已经出现了问题,其会主动修改自己的状态为 LOOKING,去查找新的 Leader。
而其它 Server 由于有过半的主机认为已经丢失了 Leader,所以它们会发起新的 Leader选举,选出一个新的 Leader。

正常情况下,当 Leader 收到超过半数 Follower 的 ACKs 后,就向各个 Follower 广播COMMIT 消息,批准各个Server 执行该写操作事务。当各个Server 在接收到Leader 的COMMIT 消息后就会在本地执行该写操作,然后会向客户端响应写操作成功。
但是如果在非全部 Follower 收到 COMMIT 消息之前 Leader 就挂了,这将导致一种后果:部分 Server 已经执行了该事务,而部分 Server 尚未收到 COMMIT 消息,所以其并没有执行该事务。当新的 Leader 被选举出,集群经过恢复模式后需要保证所有 Server 上都执行了那些已经被部分 Server 执行过的事务。

当在 Leader 新事务已经通过,其已经将该事务更新到了本地,但所有 Follower 还都没有收到 COMMIT 之前,Leader 宕机了,此时,所有 Follower 根本就不知道该 Proposal 的存在。当新的 Leader 选举出来,整个集群进入正常服务状态后,之前挂了的 Leader 主机重新启动并注册成为了 Follower。若那个别人根本不知道的 Proposal 还保留在那个主机,那么其数据就会比其它主机多出了内容,导致整个系统状态的不一致。所以,该 Proposa 应该被丢弃。类似这样应该被丢弃的事务,是不能再次出现在集群中的,应该被清除。

前面我们说过,无论是写操作投票,还是 Leader 选举投票,都必须过半才能通过,也就是说若出现超过半数的主机宕机,则投票永远无法通过。基于该理论,由 5 台主机构成的集群,最多只允许 2 台宕机。而由 6 台构成的集群,其最多也只允许 2 台宕机。即,6 台与5 台的容灾能力是相同的。基于此容灾能力的原因,建议使用奇数台主机构成集群,以避免资源浪费。
但从系统吞吐量上说,6 台主机的性能一定是高于 5 台的。所以使用 6 台主机并不是资源浪费。

对于一个高可用的系统,除了要设置多台主机部署为一个集群避免单点问题外,还需要考虑将集群部署在多个机房、多个楼宇。对于多个机房、楼宇中集群也是不能随意部署的, 下面就多个机房的部署进行分析。
在多机房部署设计中,要充分考虑“过半原则”,也就是说,尽量要确保 zk 集群中有过半的机器能够正常运行。

在生产环境下,三机房部署是最常见的、容灾性最好的部署方案。三机房部署中要求每个机房中的主机数量必须少于集群总数的一半。

zk 官方没有给出较好的双机房部署的容灾方案。只能是让其中一个机房占有超过半数的主机,使其做为主机房,而另一机房少于半数。当然,若主机房出现问题,则整个集群会瘫痪。

CAP 定理又称 CAP 原则,指的是在一个分布式系统中,Consistency(一致性)、Availability(可用性)、Partition tolerance(分区容错性),三者不可兼得。

对于分布式系统,网络环境相对是不可控的,出现网络分区是不可避免的,因此系统必须具备分区容错性。但其并不能同时保证一致性与可用性。CAP 原则对于一个分布式系统来说,只可能满足两项,即要么 CP,要么 AP。

BASE 是Basically Available(基本可用)、Soft state(软状态)和 Eventually consistent(最终一致性)三个短语的简写。

BASE 理论的核心思想是:即使无法做到实时一致性,但每个系统都可以根据自身的业务特点,采用适当的方式来使系统达到最终一致性。

基本可用是指分布式系统在出现不可预知故障的时候,允许损失部分可用性。
损失响应时间:
损失功能:

软状态,是指允许系统数据存在的中间状态,并认为该中间状态的存在不会影响系统的整体可用性,即允许系统主机间进行数据同步的过程存在一定延时。软状态,其实就是一种灰度状态,过渡状态。

最终一致性强调的是系统中所有的数据副本,在经过一段时间的同步后,最终能够达到一个一致的状态。因此,最终一致性的本质是需要系统保证最终数据能够达到一致,而不需要实时保证系统数据的一致性。

从达到一致性的时间角度来划分,可以分为:

单从客户端访问到的内容角度来划分,可以分为:

zk 遵循的是 CP 原则,即保证了一致性,但牺牲了可用性。体现在哪里呢?

当 Leader 宕机后,zk 集群会马上进行新的 Leader 的选举。但选举时长一般在 200 毫秒内,最长不超过 60 秒,整个选举期间 zk 集群是不接受客户端的读写操作的,即 zk 集群是处于瘫痪状态的。所以,其不满足可用性。

这里说的zk可能会引发脑裂,是指的在多机房部署中,若出现了网络连接问题,形成多个分区,则可能会出现脑裂问题,可能会导致数据不一致。
(1)情况一

(2)情况二

(5)情况五

Ⅳ Zookpeer是什么在系统中如何起作用

Zookeeper分布式服务框架是Apache Hadoop的一个子项目,它主要是用来解决分布式应用中经常遇到的一些数据管理问题。如:统一命名服务、状态同步服务、集群管理、分布式应用配置项的管理等。

我们先看看它都提供了哪些功能,然后再看看使用它的这些功能能做点什么。

简单的说,zookeeper=文件系统+通知机制。

Zookeeper维护一个类似文件系统的数据结构:

每个子目录项如 NameService 都被称作为 znode,和文件系统一样,我们能够自由的增加、删除znode,在一个znode下增加、删除子znode,唯一的不同在于znode是可以存储数据的。

客户端注册监听它关心的目录节点,当目录节点发生变化(数据改变、被删除、子目录节点增加删除)时,zookeeper会通知客户端。

这个似乎最简单,在zookeeper的文件系统里创建一个目录,即有唯一的path。在我们使用tborg无法确定上游程序的部署机器时即可与下游程序约定好path,通过path即能互相探索发现,不见不散了。

程序总是需要配置的,如果程序分散部署在多台机器上,要逐个改变配置就变得困难。
可以把这些配置全部放到zookeeper上去,保存在 Zookeeper 的某个目录节点中,然后所有相关应用程序对这个目录节点进行监听,一旦配置信息发生变化,每个应用程序就会收到 Zookeeper 的通知,然后从 Zookeeper 获取新的配置信息应用到系统中就好。

集群管理无在乎两点:是否有机器退出和加入、选举master。

对于第一点,所有机器约定在父目录GroupMembers下创建临时目录节点,然后监听父目录节点的子节点变化消息。一旦有机器挂掉,该机器与 zookeeper的连接断开,其所创建的临时目录节点被删除,所有其他机器都收到通知:某个兄弟目录被删除,于是,所有人都知道:它下船了。当然又会有新机器加入,也是类似:所有机器收到通知---新兄弟目录加入,highcount又有了,有人上船了。

对于第二点,我们假设机器创建临时顺序编号目录节点,每次选取编号最小的机器作为master就好。

有了zookeeper的一致性文件系统,锁的问题变得容易。锁服务可以分为两类,一个是保持独占,另一个是控制时序。

对于第一类,我们将zookeeper上的一个znode看作是一把锁,通过createznode的方式来实现。所有客户端都去创建 /distribute_lock 节点,最终成功创建的那个客户端也即拥有了这把锁。厕所有言:来也冲冲,去也冲冲,用完删除掉自己创建的distribute_lock 节点就释放出锁。

对于第二类, /distribute_lock 已经预先存在,所有客户端在它下面创建临时顺序编号目录节点,和选master一样,编号最小的获得锁,用完删除,依次方便。

两种类型的队列:

1、 同步队列,当一个队列的成员都聚齐时,这个队列才可用,否则一直等待所有成员到达。

2、队列按照 FIFO 方式进行入队和出队操作。

第一类,在约定目录下创建临时目录节点,监听节点数目是否是我们要求的数目。

第二类,和分布式锁服务中的控制时序场景基本原理一致,入列有编号,出列按编号。

Zookeeper中的角色主要有以下三类:

系统模型如图所示:

Zookeeper的核心是原子广播,这个机制保证了各个Server之间的同步。实现这个机制的协议叫做Zab协议。Zab协议有两种模式,它们分 别是恢复模式(选主)和广播模式(同步)。当服务启动或者在领导者崩溃后,Zab就进入了恢复模式,当领导者被选举出来,且大多数Server完成了和 leader的状态同步以后,恢复模式就结束了。状态同步保证了leader和Server具有相同的系统状态。

为了保证事务的顺序一致性,zookeeper采用了递增的事务id号(zxid)来标识事务。所有的提议(proposal)都在被提出的时候加上 了zxid。实现中zxid是一个64位的数字,它高32位是epoch用来标识leader关系是否改变,每次一个leader被选出来,它都会有一个 新的epoch,标识当前属于那个leader的统治时期。低32位用于递增计数。

每个Server在工作过程中有三种状态:

当leader崩溃或者leader失去大多数的follower,这时候zk进入恢复模式,恢复模式需要重新选举出一个新的leader,让所有的 Server都恢复到一个正确的状态。Zk的选举算法有两种:一种是基于basic paxos实现的,另外一种是基于fast paxos算法实现的。系统默认的选举算法为fast paxos。先介绍basic paxos流程:

通过流程分析我们可以得出:要使Leader获得多数Server的支持,则Server总数必须是奇数2n+1,且存活的Server的数目不得少于n+1.

选完leader以后,zk就进入状态同步过程。

Leader主要有三个功能:

PING消息是指Learner的心跳信息;REQUEST消息是Follower发送的提议信息,包括写请求及同步请求;ACK消息是 Follower的对提议的回复,超过半数的Follower通过,则commit该提议;REVALIDATE消息是用来延长SESSION有效时间。
Leader的工作流程简图如下所示,在实际实现中,流程要比下图复杂得多,启动了三个线程来实现功能。

Follower主要有四个功能:

Follower的消息循环处理如下几种来自Leader的消息:

Follower的工作流程简图如下所示,在实际实现中,Follower是通过5个线程来实现功能的。

https://blog.csdn.net/xinguan1267/article/details/38422149
https://blog.csdn.net/gs80140/article/details/51496925
https://www.2cto.com/kf/201708/668587.html
https://blog.csdn.net/milhua/article/details/78931672

P.S. 这篇文章是本人对网络上关于ZK的文章阅读之后整理所得,作为入门级的了解。个人觉得看了上面的内容就能基本了解Zookeeper的作用了,后面在结合实际项目使用加深自己的了解。

end

热点内容
我的世界怎样刷出32k服务器 发布:2024-05-18 14:32:32 浏览:565
c语言程序设计江宝钏 发布:2024-05-18 14:32:22 浏览:780
右击文件夹总是转圈圈 发布:2024-05-18 14:31:10 浏览:695
新建数据库phpmyadmin 发布:2024-05-18 14:22:38 浏览:735
安卓手机设备连接在哪里 发布:2024-05-18 14:08:28 浏览:819
路由器的密码最多是多少位 发布:2024-05-18 13:58:18 浏览:419
扫描服务器名称如何填 发布:2024-05-18 13:36:29 浏览:114
芒果缓存的视频看不了视频怎么下载不了 发布:2024-05-18 13:35:14 浏览:519
c语言发短信 发布:2024-05-18 13:23:08 浏览:834
vb数据库程序 发布:2024-05-18 13:01:57 浏览:113