Raft学习笔记【三】——复制日志

本文讲述了客户端如何在分布式系统中找到并与leader通信,以及leader如何处理client的指令,包括日志一致性检查、follower状态管理和冲突解决策略。重点介绍了Raft算法在处理follower落后和日志冲突时的机制。
摘要由CSDN通过智能技术生成

前言

我是一个没有感谢的学习机器人

client如何找到leader

在我们的集群中,有这么一个大家可能都会想到的问题,就是:客户端去访问你的分布式系统时,怎么找到对应的leader呢?我又不知道你们内部选举的是谁?

其实对于这个问题,一个非常简单的解决方案就是,集群不在乎客户端访问的是哪一个节点。因为无论客户端访问的是哪个节点,对应地节点都可以非常简单地找到leader并发送给服务端。

假设客户端之前一直是和老leader建立的连接,但是因为一些内部原因,这个老leader结束了自己的任期,集群内产生了新的leader。新的leader会通过心跳机制给所有的follower发送保持连接的RPC,而该RPC里有新leader自己的ID。那么当客户端访问一个非leader节点,该节点可以轻易地告诉客户端现在的leader是谁,客户端再去连接新的leader节点即可。

当然,假如客户端访问的节点宕机了,就无法获得新leader的信息,这时就需要客户端自己重新换一个节点连接,因为分布式系统只要半数以上的节点工作正常就能提供服务,多试几次总能找到正常运行的节点,也就可以获得leader信息进行访问了。


client给leader发送指令的时候leader做了什么

在集群内部,每个节点会维护一个日志,日志中的条目存放了用户的指令。但是注意,节点有这个日志条目不代表节点就接收了这个指令更新了数据,该日志仅仅是用来做一致性检查的。

我们来模拟一遍日志一致性检查的过程。

  1. 客户端client给leader发送指令
  2. leader记录该指令,并将该指令绑定到一个日志号上,再结合自己的leader日期号封装成一个日志条目Entry
  3. leader将该日志条目通过AppendEntriesRequest RPC,将其并行地分发给follower节点
  4. follower节点将该日志记录在自己地本地后,给leader发去成功的AppendEntriesResponse RPC
  5. leader收到半数以上的AppendEntriesResponse RPC后,将该指令apply到状态机
  6. leader反馈给客户端client指令成功


如果follower的日志落后了怎么办?

我们考虑这样一个问题,在集群中某个节点因为某些原因宕机了一段时间,等它恢复的时候,已经比现在集群中的日志落后了非常多怎么办?如下图:

可以看到,图中leader已经经历了3任,现在已经是第三任leader了,而follower2在第一任任期的时候就宕机了,等它恢复过来已经到了第三任leader,这时候怎么办呢?

raft算法需要保证数据的一致性,所以不能抛弃任何一个节点,比较在分布式节点较多的时候,部分节点宕机是比较正常的现象。因此在follower2“昏迷”期间,leader会持续给他发送AppendEntrieRequest,第二任leader任期到了之后,新任期的leader依然要发送,直到follwer2恢复正常状态。

但是,当follower2恢复正常状态之后,它会对比leader给自己的AppendEntrieRequest中的日志号,当与当前本地号不一致时,它会拒绝该日志并给leader发送一个拒绝通知。而leader的AppendEntriesRequest中会保存上一个日志条目的索引号和任期号(可以看看上一节图中的RPC内部数据),leader就会去找上一条日志发送给follower,一直往上查找直到找到follower当前的日志号,然后该follower便添加该日志,更新状态机,直到追上leader 的进度。

可以看出来,在这个过程中,该follower不会影响其他follower的正常工作。所以部分follower哪怕日志落了,也不影响整个系统的运行效率。

当然,也可以看出这种算法效率比较低;其实具体实现的时候完全可以恢复过来follower把自己最新的日志告诉leader,leader从该日志号开始给日志发送AppendEntrieRequest。那么这种算法是raft的作者考虑不周吗?其实不是,作者在论文中有提到,节点宕机的现象其实是很少的,所以他们认为这里的具体实现并不重要。当然,落地到工程中,我们可以根据需求修改这里的逻辑。


如果follower的日志比leader更大怎么办?

follower比leader的日志更小好理解,那么follower比leader更大是什么情况呢?我们来模拟一下这种情况:

  1. 节点1当选leader,收到客户端的指令a,b
  2. 节点1将该指令更新到自己的日志中,然后并发给follower发送AppendEntriesRequest RPC,但是因为网络延迟,这些信息并没有发送到follower手上
  3. followers内部看迟迟没有消息,选出了新的leader节点2
  4. 节点2收到客户端指令c,将该指令更新到自己的日志中,然后并发给followers发送AppendEntrieRequest RPC,节点1收到该日志,对比发现自己的日志中有a,b,日志号比该日志更大

可以看到,虽然节点1的日志更新了a和b,但是因为并没有半数以上的follower收到这两条日志,所以这两条日志并没有apply到状态机,所以当新的leader出现时,这些没有apply的日志就被新的日志给强行覆盖了。raft这种做法简单且暴力,但是却并没有违反它的一致性。因为我们知道,只有当半数以上的节点更新了日志,leader才会将这条日志的命令apply到状态机,这在具体实现中,可以实现为commitIndex指针对应日志,applyIndex对应状态机,applyIndex总数小于等于commitIndex的。


结束语

下一章讲一下安全性和集群成员变更,raft差不读就学习完毕了

  • 19
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值