Raft一致性算法(13)


安装快照 RPC:
由领导人调用以将快照的分块发送给跟随者 。领导人总是按顺序发送分块 。
参数
解释
term
领导人的任期号
leaderId
领导人的 ID,以便于跟随者重定向请求
lastIncludedIndex
快照中包含的最后日志条目的索引值
lastIncludedTerm
快照中包含的最后日志条目的任期号
offset
分块在快照中的字节偏移量
data[]
从偏移量开始的快照分块的原始字节
done
如果这是最后一个分块则为 true
结果
解释
term
当前任期号(currentTerm),便于领导人更新自己
接收者实现:
 

  1. 如果term < currentTerm就立即回复
  2. 如果是第一个分块(offset 为 0)就创建一个新的快照
  3. 在指定偏移量写入数据
  4. 如果 done 是 false,则继续等待更多的数据
  5. 保存快照文件,丢弃具有较小索引的任何现有或部分快照
  6. 如果现存的日志条目与快照中最后包含的日志条目具有相同的索引值和任期号,则保留其后的日志条目并进行回复
  7. 丢弃整个日志
  8. 使用快照重置状态机(并加载快照的集群配置)
 
Raft一致性算法

文章插图
 
 
图 13:一个关于安装快照的简要概述 。为了便于传输,快照都是被分成分块的;每个分块都给了跟随者生命的迹象,所以跟随者可以重置选举超时计时器 。
 
在这种情况下领导人使用一种叫做安装快照的新的 RPC 来发送快照给太落后的跟随者;见图 13 。当跟随者通过这种 RPC 接收到快照时,他必须自己决定对于已经存在的日志该如何处理 。通常快照会包含没有在接收者日志中存在的信息 。在这种情况下,跟随者丢弃其整个日志;它全部被快照取代,并且可能包含与快照冲突的未提交条目 。如果接收到的快照是自己日志的前面部分(由于网络重传或者错误),那么被快照包含的条目将会被全部删除,但是快照后面的条目仍然有效,必须保留 。
这种快照的方式背离了 Raft 的强领导人原则,因为跟随者可以在不知道领导人情况下创建快照 。但是我们认为这种背离是值得的 。领导人的存在,是为了解决在达成一致性的时候的冲突,但是在创建快照的时候,一致性已经达成,这时不存在冲突了,所以没有领导人也是可以的 。数据依然是从领导人传给跟随者,只是跟随者可以重新组织他们的数据了 。
我们考虑过一种替代的基于领导人的快照方案,即只有领导人创建快照,然后发送给所有的跟随者 。但是这样做有两个缺点 。第一,发送快照会浪费网络带宽并且延缓了快照处理的时间 。每个跟随者都已经拥有了所有产生快照需要的信息,而且很显然,自己从本地的状态中创建快照比通过网络接收别人发来的要经济 。第二,领导人的实现会更加复杂 。例如,领导人需要发送快照的同时并行的将新的日志条目发送给跟随者,这样才不会阻塞新的客户端请求 。
还有两个问题影响了快照的性能 。首先,服务器必须决定什么时候应该创建快照 。如果快照创建的过于频繁,那么就会浪费大量的磁盘带宽和其他资源;如果创建快照频率太低,他就要承受耗尽存储容量的风险,同时也增加了从日志重建的时间 。一个简单的策略就是当日志大小达到一个固定大小的时候就创建一次快照 。如果这个阈值设置的显著大于期望的快照的大小,那么快照对磁盘压力的影响就会很小了 。
第二个影响性能的问题就是写入快照需要花费显著的一段时间,并且我们还不希望影响到正常操作 。解决方案是通过写时复制的技术,这样新的更新就可以被接收而不影响到快照 。例如,具有函数式数据结构的状态机天然支持这样的功能 。另外,操作系统的写时复制技术的支持(如 linux 上的 fork)可以被用来创建完整的状态机的内存快照(我们的实现就是这样的) 。
8 客户端交互
这一节将介绍客户端是如何和 Raft 进行交互的,包括客户端如何发现领导人和 Raft 是如何支持线性化语义的 。这些问题对于所有基于一致性的系统都存在,并且 Raft 的解决方案和其他的也差不多 。
Raft 中的客户端发送所有请求给领导人 。当客户端启动的时候,他会随机挑选一个服务器进行通信 。如果客户端第一次挑选的服务器不是领导人,那么那个服务器会拒绝客户端的请求并且提供他最近接收到的领导人的信息(附加条目请求包含了领导人的网络地址) 。如果领导人已经崩溃了,那么客户端的请求就会超时;客户端之后会再次重试随机挑选服务器的过程 。


推荐阅读