实现拜占庭容错的Raft算法-总结

拜占庭容错的Raft总结

基本前提:

n个节点中存在f个拜占庭节点, 选举和日志提交需要quorum个节点的确认

(n-f)/2 -f>f 半数以上的正常节点的共识不会被f个拜占庭节点推翻

quorum -f> (n-f)/2

得出n=5f+1. quorum=3f+1

共识结点, 客户端, 互相持有对方公钥

日志篡改

客户端发给Leader的指令被Leader篡改, Follower篡改添加的指令

Follower检查到AppendEntries的签名与实际不符, 立即开始新一轮选举

Follower对添加日志的篡改不会产生影响

日志篡改详细过程

Follower被动接受Leader的AppendEntries消息, 如果Leader是拜占庭节点, 客户端发送请求可能被篡改, Follower可能篡改添加的消息, BRaft利用数字签名解决日志篡改

Leader篡改

客户端发送给Leader的消息除了待执行的指令, 还包含对指令的数字签名, Leader向Follower发送日志, 如果此时Leader篡改, 收到消息的Follower可以根据数据签名检测出日志项已经被篡改, 并拒绝添加日志, 然后该Follower转换状态为Candidate开启新一轮Leader选举

如果Leader向少于quorum个节点发送篡改的日志, 则并不影响使用, 因为此时发起竞选的节点并没有真正的最新日志, 收到正常最新日志的节点不会给他投票

Follower篡改

Follower篡改了日志不会对安全性造成影响, 因为BRaft中剩下的节点依然能达成共识, 由于拜占庭Follower并不持有真正的最新日志, 它将不具备成为leader的能力

Leader选举

候选人伪造持有最新的日志骗取选票

先发一次PreRequestVote, Follower响应自身最新的已提交日志Term, Index作为验证要求, 然后Leader发送RequestVote, 其中带上自己算出来的要求日志hash, Follower校验后进行投票

选举详细过程

节点收到RequestVote消息后不能仅通过校验逻辑时间戳来决定是否进行投票, 候选人可以伪造持有更新的日志骗取选票, BRaft的Leader选举机制包含三个阶段

1 投票消息发送, 与Raft投票的第一阶段类似, 投票消息中包含candidat日志列表的最后一项已经达成共识的日志项的逻辑时间戳 此阶段候选人可能伪造这个时间戳

2 如果节点收到1的投票消息, 首先检查消息中的逻辑时间戳是否比自己更新, 如果不新则直接拒绝响应, 随后Follower将自己最后一个达成共识的日志项的逻辑时间戳作为响应消息参数发给Candidate, 接下来Candidate需要向Follower证明自己确实持有比投票节点更完备的日志列表 (Committed证明请求消息)

3 Candidate收到Committed证明请求消息后, 如果Candidate确实拥有证明请求消息中逻辑时间戳对应的Term和Index日志项, 则Candidate将该日志项的哈希值作为响应参数返回 (利用SHA256签名), 当节点收到响应消息后, 检测哈希值是否与自己持有日志项的哈希值一致, 如果一致则投票, 否则拒绝

在第一阶段, Candidate向其他节点发送PreRequestVoteRPC, 其他节点回复要求证明的日志项逻辑时间戳作为响应, 第二阶段Candidate计算出要求的哈希值, 作为Committed证明值包含在RequestVoteRPC请求中, 发送到Follower节点, 此时再决定是否投票

BRaft算法增加了一轮通信, 但仍保持着On的通信复杂度, 此阶段修改不会影响性能

日志提交

Follower不相信Leader持有的最新已提交日志CommitIndex, Leader不相信Follower已将分发的日志保存

Follower不再根据AppendEntries中Leader的CommitIndex更新自己的已提交日志项, 而是自行发送AppendEntriesCommit广播, 告知其他节点”某条日志已经被我收到”, 同时也接收其他节点的接收广播, 然后根据已提交的数量更新自己本地的CommitIndex, Leader也不再根据回复更新CommitIndex, 而是从自己接收的AppendEntriesCommit信息中更新本地map, 进而推进CommitIndex

日志提交详细过程

Leader节点尝试将日志同步给Follower节点, 通过sendAppendEntries函数发送日志条目。

Follower节点在收到AppendEntriesArgs对象后,在AppendEntries函数中处理这个请求, 检查请求中的term是否大于自己的currentTerm, 然后,Follower节点会检查请求中的日志条目是否是自己需要的下一条日志,如果是,那么Follower节点发起一轮AppendEntrisCommitRPC, 写入一个CommitKey中, 并告知Leader添加成功

此后Leader和Follower均依靠AppendEntrisCommit更新本地map, 从而确认CommitIndex, 每次CommitIndex推进发送signal通知应用状态机推进日志应用

当且仅当节点收到了quorum数量的针对某一Index, Term, 和哈希值的AppendEntriesCommit消息后, 可以确认该日志项已经在集群中达成共识, 每个节点会保存从其他节点收到的AppendEntriesCommit消息, 当节点确认特点Index和Term的日志项已经达成共识, 删除该Index之前的日志项对应的AppendEntriesCommit消息

处理日志不一致

BRaft采用与基本Raft类似方式处理, Raft算法中Leader设置NextIndex数组记录每个Follower下一条待复制的日志index, 在日志追加中Follower响应最后一条本地Committed的日志记录, Leader逐步回退

由于拜占庭节点的存在可能发生两种场景

1 Leader是拜占庭节点, 数字签名可以保证Follower不把篡改的日志项添加进日志列表, BRaft采用回退处理日志列表不匹配的问题

2 Follower是拜占庭节点, 可以发起两种攻击行为

拒绝添加日志: 该节点将缺少集群中已经达成共识的日志项, 因此不具备成为Leader的能力, 共识安全性可以保证

篡改日志: 如果被篡改的日志已经达成共识, 且此时拜占庭节点成为Leader, 它在试图覆盖此日志时会被已持有日志的Follower拒绝

客户端交互(未实现)

恶意Leader节点可以拒绝客户端请求, 因此BRaft的客户端交互不再仅依赖Leader, 而是所有参与共识的节点参与; 包含四个阶段

1 客户端向BRaft集群发送请求, 如果收到请求的节点不是Leader则转发请求给Leader

2 Leader收到请求后执行日志复制操作

3 Leader和Follower将日志传递给状态机执行后, 各自将执行结果响应给客户端

4 如果客户端收到f+1个一致的响应, 则将其视为正确的响应

BRaft中任意节点需要给返回客户端的响应进行数字签名, 客户端持有所有节点的公钥, 它负责校验所有响应确保响应的消息未被篡改且每个节点只响应一次

  • 15
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值