赞
踩
CAP : 一个分布式系统不可能同时满足一致性 (C: Consistency)、可用性(A:Availability)和分区容错性(P:Partition tolerance)这三个基本需求,最多只能同时满足其中的两项
分布式存储架构中,设计共识算法要考虑在一个复制集群中,所有节点按照确认的顺序处理命令,最终结果是在客户端看来,一个复制集群表现的像一个单状态机一样一致。为了保证多个节点顺序一致,需要处理如下问题:
CAP : 一个分布式系统不可能同时满足一致性 (C: Consistency)、可用性(A:Availability)和分区容错性(P:Partition tolerance)这三个基本需求,最多只能同时满足其中的两项
分布式存储架构中,设计共识算法要考虑在一个复制集群中,所有节点按照确认的顺序处理命令,最终结果是在客户端看来,一个复制集群表现的像一个单状态机一样一致。为了保证多个节点顺序一致,需要处理如下问题:
由于分布式系统引入了多个节点,节点规模越大,宕机、网络时延、网络分区就会成为常态,任何一个问题都可能导致节点之间的数据不一致,Raft 是用来解决分布式场景下的一致性问题的共识算法。
举例来说有5台机器组成的集群,需要保证机器之间数据同步,有如下问题需要考虑:
这些问题都是分布式系统经常会面临的问题。Raft 一致性算法可以保证,即使在网络丢包,延迟,乱序,或者宕机,部分网络隔离的情况下,集群中机器能保证数据的强一致性。
Raft的核心就是leader发出日志同步请求,follower接收并同步日志,最终保证整个集群的日志一致性。:
Raft 是满足 CAP 定理中的 C 和 P 特性,属于强一致性的分布式协议。
Raft 的三个核心问题:
所以,Raft算法核心流程可以归纳为:
Raft 的优点如下:
节点的状态切换状态机如下图所示
Raft 节点间通过 RPC 请求来互相通信,主要有以下两类 RPC 请求:
每个任期开始都会进行领导选举(Leader Election)。如果选举成功,则进入维持任务Term阶段,此时Leader负责接收客户端请求并,负责复制日志。Leader和所有Follower都保持通信,如果Follower发现通信超时(网络问题、或者Leader宕机),会触发TermId递增并发起新的选举。如果选举成功,则进入新的任期。如果选举失败(Hint:选举失败触发的条件是什么?),没选出Leader,则当前任期很快结束,TermId递增,然后重新发起选举直到成功。
如下图,Term N选举成功,Term N+1和Term N+2选举失败,Term N+3重新选举成功
在Raft协议中,任期充当逻辑时钟,服务器节点可以通过任期发现一些过期的信息,比如过时的 Leader。任期单调递增,服务器之间通信的时候会交换当前任期号;如果一个服务器的当前任期号比其他的小,该服务器会将自己的任期号更新为较大的那个值。如果一个 Candidate 或 Leader 发现自己的任期号过期了,它会立即回到 Follower 状态。如果一个节点接收到一个包含过期的任期号的请求,它会直接拒绝这个请求.
在 Raft 中有两个 Timeout 定时器控制着选主 Election 的进行:
简言之:
当 Leader 节点由于异常(宕机、网络故障等)无法继续提供服务时,可以认为它结束了本轮任期(TERM=N),需要开始新一轮的选举(Election),而新的 Leader 当然要从 Follower 中产生,开始新一轮的任期(TERM=N+1)。(从 Follower 节点的角度来看,当它接收不到 Leader 节点的心跳即心跳超时时间触发时,可认为 Leader 已经丢失,便进入新一轮的选举流程)
一个 Follower 的竞选过程如下:
case1:本 Node(自己)赢得选举
只有当一个 Candidate 得到 超过半数选票时才能赢得选举 ,每一个节点按照先到先得的方式,最多投票给一位 Candidate。在 Candidate 赢得选举后,自己变为 Leader,同时向所有节点发送心跳信息以使其他节点变为 Follower,本次选举结束,开始下一任任期
case2:其他 Node 赢得选举
在等待投票结果的过程中,如果 Candidate 收到其他节点发送的心跳信息(即 AppendEntries RPC), 并检查此心跳信息中的任期不比自己小(这一点很重要),则自己变为 Follower,听从新上任的 Leader 的指挥
case3: 本轮选举未产生结果(一段时间之后没有任何获胜者)
极端情况下,如当有多个 Candidate 同时竞选时,由于每个人先为自己投一票,导致没有任何一个人的选票数量过半。当这种情况出现时,每位 Candidate 都开始准备下一任竞选:将 TERM+=1,同时再次发送拉票请求。为了防止出现长时间选不出新 Leader 的情况,Raft 采用了两个方法来尽可能规避该情况发生(防止第3种情况无限重复,使得没有Leader被选举出来):
Follower 认为 Leader 不可用的超时时间(Election Timeout,即选举超时时间)是随机值,防止了所有的 Follower 都在同一时刻发现 Leader 不可用的情况,从而让先发现的 Follower 顺利成为 Candidate,继而完成剩下的选举过程并当选(使用随机化选举超时时间,从一个固定的区间如150-300ms随机选择,避免同时出现多个Candidate)
即使出现多个 Candidate 同时竞选的情况,再发送拉票请求时,也有一段随机的延迟,来确保各个 Candidate 不是同时发送拉票请求(Candidate等待超时的时间随机:Candidate 在开始一次选举的时候会重置一个随机的选举超时时间,然后一直等待直到选举超时,这样减小了在新的选举中再次发生选票瓜分情况的可能性)
集群中每个节点只能处于Leader、Follower和Candidate三种状态的一种,从节点视角看选举流程如下:
那么,什么时候选举重新发生呢?
在选举完成后,Leader和所有Follower都保持通信,如果Follower发现通信超时,任期ID(TermId)递增并发起新的选举。如果选举成功,则进入新的任期。如果选举失败,TermId递增,然后重新发起选举直到成功。
上面说了,Leader在任期内会周期性向其他Follower节点发送心跳来维持地位,Follower如果发现心跳超时,就认为Leader节点宕机或不存在。随机等待一定时间后,Follower会发起选举,变成Candidate,然后去竞选Leader。重新选举结果有三种情况:
脑裂问题(Brain Split),即任一任期Term内有超过一个Leader被选出(Raft要求在一个集群中任何时刻只能有一个Leader),这是非常严重的问题,会导致数据的覆盖丢失。
出现的场景,如下面的Raft集群,某个时刻出现网络分区,集群被隔开成两个网络分区,在不同的网络分区里会因为无法接收到原来的Leader发出的心跳而超时选主,这样就会造成多Leader现象。
在网络分区1和2中,出现了两个Leader:A和D,假设此时要更新分区2的值,因为分区2无法得到集群中的大多数节点的ACK,会导致复制失败。而网络分区1会成功,因为分区1中的节点更多,Leader A能得到大多数回应。
当网络恢复的时候,集群网络恢复,不再是双分区,Raft会有如下操作:
小结下,领导选举流程通过若干的投票原则,保证一次选举有且仅可能最多选出一个Leader,从而解决了脑裂问题
每当Leader对所有的Follower发出Append Entries的时候,Follower会有一个随机的超时时间,如果超时TTL内收到了Leader的请求就会重置超时TTL;如果超时后Follower仍然没有收到 Leader的心跳,Follower会认为 Leader 可能已经离线,此时第一个超时的Follower会发起投票,这个时候它依然会向宕机的原Leader发出Reuest Vote(原Leader不会回复)。当收到集群超过一半的节点的RequestVote reply后,此时的Follower会成为Leader
若稍后宕机的Leader恢复正常之后,重新加入到Raft集群,初始化的角色是Follower(非leader)
Follower宕机对整个集群影响不大(可用个数内),影响是Leader发出的Append Entries无法被收到,但是Leader还会继续一直发送,直到Follower恢复正常。Raft协议会保证发送AppendEntries request的rpc消息是幂等的:即如果Follower已经接受到了消息,但是Leader又让它再次接受相同的消息,Follower会直接忽略
Raft集群建立完成后,Leader接收所有客户端请求,然后转化为log复制命令,发送通知其他节点完成日志复制请求。每个日志复制请求包括状态机命令 & 任期号,同时还有前一个日志的任期号和日志索引。状态机命令表示客户端请求的数据操作指令,任期号表示Leader的当前任期。
日志由有序编号的日志条目组成。每个日志条目包含它被创建时的任期号(每个方块中的数字),并且包含用于状态机执行的命令。如果一个条目能够被状态机安全执行,就被认为可以提交了,如下图:
Leader 决定什么时候将日志条目应用到状态机是安全的;这种条目被称为是已提交的(Committed)
。Raft 保证可已提交的日志条目是持久化的,并且最终会被所有可用的状态机执行。一旦被 Leader 创建的条目已经复制到了大多数的服务器上,这个条目就称为已提交的(上图中的7号条目)。Leader 日志中之前的条目都是已提交的,包括由之前的 Leader 创建的条目。
Leader 跟踪记录它所知道的已提交的条目的最大索引值,并且这个索引值会包含在之后的 AppendEntries RPC 中(包括心跳中),为的是让其他服务器都知道这个条目已经提交。一旦一个 Follower 知道了一个日志条目已经是已提交的,它会将该条目应用至本地的状态机(按照日志顺序)
日志同步的概念:服务器接收客户的数据更新/删除请求,这些请求会落地为命令日志。只要输入状态机的日志命令相同,状态机的执行结果就相同。Raft中是Leader发出日志同步请求,Follower接收并同步日志,最终保证整个集群的日志一致性。
在了解日志同步前,先了解复制状态机的概念。复制状态机(Replicated state machines)是指不同节点从相同的初始状态出发,执行相同顺序的输入指令集后,会得到相同的结束状态。
分布式一致性算法的实现是基于复制状态机的。在Raft算法中,节点初始化后具有相同初始状态。为了提供相同的输入指令集这个条件,Raft将一个客户端请求(command)封装到一个Log Entry中。Leader负责将这些Log Entries 复制到所有的Follower节点,然后节点按照相同的顺序应用commands,从而达到最终的一致状态。
如果在不同日志中的两个条目有着相同日志索引index和任期号termid,则所存储的命令是相同的,这点是由Leader来保证的
如果在不同日志中的两个条目有着相同日志索引index和任期号termid,则它们之间所有条目完全一样,这点是由日志复制的规则来保证的
当Leader收到客户端的写请求,到将执行结果返回给客户端的这个过程,从Leader视角来看经历了以下步骤:
从Follower视角来看经历了如下步骤,Follower收到日志复制请求的处理流程
Logs由顺序排列的Log Entry组成 ,每个Log Entry包含command和产生该Log Entry时的Leader Term。从上图可知,五个节点的日志并不完全一致,Raft算法为了保证高可用,并不是强一致性,而是最终一致性,Leader会不断尝试给Follower发Log Entries,直到所有节点的Log Entries都相同。
在前面的流程中,Leader只需要日志被复制到大多数节点即可向客户端返回,而一旦向客户端返回成功消息,那么系统就必须保证log在任何异常的情况下都不会发生回滚。
从图中看,LogIndex 1-4的日志已经完成同步,LogIndex 5正在同步,LogIndex 6还未开始同步,下一小节会基于官方文档完整的描述日志复制的过程
日志不一致问题:在正常情况下,Leader和Follower的日志复制能够保证整个集群的一致性,但是遇到Leader宕机时,Leader和Follower日志可能出现了不一致,一般而言此时Follower相比Leader缺少部分日志。
为了解决数据不一致性,Raft算法规定Follower强制复制Leader节点的日志,即Follower不一致日志都会被Leader的日志覆盖,最终Follower和Leader保持一致。
这个强制复制指的是即从前向后寻找Follower和Leader第一个公共LogIndex的位置,然后从这个位置开始,Follower强制复制Leader的日志。这里的复制动作受到下面的Raft安全性规则约束(一切以数据最终一致性为前提)
在进行一致性复制的过程中,假如出现了异常情况,raft都是如何处理的呢?
正常操作期间,Leader 和 Follower 的日志保持一致,方格数字表示任期。所以 AppendEntries RPC 的一致性检查从来不会失败。但也有可能出现下图的故障:
当一个 Leader 成功当选时(最上面那行),Follower 的状态机可能是(a-f)中的任何情况。Follower 可能会缺少一些日志条目(如a、b),可能会有一些未被提交的日志条目(如c、d),或者两种情况都存在(e、f)。例如,场景 f 可能这样发生,f 对应的服务器在任期 2 的时候被选举为 Leader ,追加了一些日志条目到自己的日志中,一条都还没提交(commit)就宕机了;该服务器很快重启,在任期 3 重新被选为 Leader,又追加了一些日志条目到自己的日志中;在这些任期 2 和任期 3 中的日志都还没被提交之前,该服务器又宕机了,并且在接下来的几个任期里一直处于宕机状态。那么,新任Leader该如何同步日志呢?
在Raft中,Leader 通过强制 Follower 复制它的日志来解决不一致的问题。这意味着 Follower 中跟 Leader 冲突的日志条目会被 Leader 的日志条目覆盖。
优化:如果想要的话,Raft协议可以被优化来减少被拒绝的 AppendEntries RPC 的个数。例如,当拒绝一个 AppendEntries RPC 的请求的时候,Follower 可以包含冲突条目的任期号和自己存储的那个任期的第一个 index。借助这些信息,Leader 可以跳过那个任期内所有冲突的日志条目来减小 nextIndex;这样就变成每个有冲突日志条目的任期需要一个 AppendEntries RPC 而不是每个条目一次。
dfs.datanode.du.reserved
?有。
因为HDFS的设计并不会在存储接近满时停止写入操作,并且因为Non DFS Used
存在,可能会导致DFS Used
比真实的磁盘使用率小,从而导致DFS Used
还没有100%,但是磁盘使用率100%,数据写入失败。
通常需要,设置dfs.datanode.du.reserved
,至少预留200g(根据实际情况而定),并监控磁盘的使用率,及时清理数据或者进行数据均衡。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。