基于 raft 协议的 RocketMQ DLedger 多副本日志复制设计原理

本文涉及的产品
日志服务 SLS,月写入数据量 50GB 1个月
简介: 上一篇 源码分析 RocketMQ DLedger(多副本) 之日志复制(传播) ,可能有不少读者朋友们觉得源码阅读较为枯燥,看的有点云里雾里,本篇将首先梳理一下 RocketMQ DLedger 多副本关于日志复制的三个核心流程图,然后再思考一下在异常情况下如何保证数据一致性。

上一篇 源码分析 RocketMQ DLedger(多副本) 之日志复制(传播) ,可能有不少读者朋友们觉得源码阅读较为枯燥,看的有点云里雾里,本篇将首先梳理一下 RocketMQ DLedger 多副本关于日志复制的三个核心流程图,然后再思考一下在异常情况下如何保证数据一致性。

1、RocketMQ DLedger 多副本日志复制流程图

1.1 RocketMQ DLedger 日志转发(append) 请求流程图

在这里插入图片描述

1.2 RocketMQ DLedger 日志仲裁流程图

在这里插入图片描述

1.3 RocketMQ DLedger 从节点日志复制流程图

在这里插入图片描述

2、RocketMQ DLedger 多副本日志复制实现要点

在这里插入图片描述
上图是一个简易的日志复制的模型:图中客户端向 DLedger 集群发起一个写请求,集群中的 Leader 节点来处理写请求,首先数据先存入 Leader 节点,然后需要广播给它的所有从节点,从节点接收到 Leader 节点的数据推送对数据进行存储,然后向主节点汇报存储的结果,Leader 节点会对该日志的存储结果进行仲裁,如果超过集群数量的一半都成功存储了该数据,主节点则向客户端返回写入成功,否则向客户端写入写入失败。

接下来我们来探讨日志复制的核心设计要点。

2.1 日志编号

为了方便对日志进行管理与辨别,raft 协议为一条一条的消息进行编号,每一条消息达到主节点时会生成一个全局唯一的递增号,这样可以根据日志序号来快速的判断数据在主从复制过程中数据是否一致,在 DLedger 的实现中对应 DLedgerMemoryStore 中的 ledgerBeginIndex、ledgerEndIndex,分别表示当前节点最小的日志序号与最大的日志序号,下一条日志的序号为 ledgerEndIndex + 1 。

与日志序号还与一个概念绑定的比较紧密,即当前的投票轮次。

2.2 追加与提交机制

请思考如下问题,Leader 节点收到客户端的数据写入请求后,通过解析请求,提取数据部分,构建日志对象,并生成日志序号,用 seq 表示,然后存储到 Leader 节点中,然后将日志广播(推送)到其从节点,由于这个过程中存在网络时延,如果此时客户端向主节点查询 seq 的日志,由于日志已经存储在 Leader 节点中了,如果直接返回给客户端显然是有问题的,那该如何来避免这种情况的发生呢?

为了解决上述问题,DLedger 的实现(应该也是 raft 协议的一部分)引入了已提交指针(committedIndex)。即当主节点收到客户端请求时,首先先将数据存储,但此时数据是未提交的,此过程可以称之为追加,此时客户端无法访问,只有当集群内超过半数的节点都将日志追加完成后,才会更新 committedIndex 指针,得以是数据能否客户端访问。

一条日志要能被提交的充分必要条件是日志得到了集群内超过半数节点成功追加,才能被认为已提交。

2.3 日志一致性如何保证

从上文得知,一个拥有3个节点的 DLedger 集群,只要主节点和其中一个从节点成功追加日志,则认为已提交,客户端即可通过主节点访问。由于部分数据存在延迟,在 DLedger 的实现中,读写请求都将由 Leader 节点来负责。那落后的从节点如何再次跟上集群的步骤呢?

要重新跟上主节点的日志记录,首先要知道的是如何判断从节点已丢失数据呢?

DLedger 的实现思路是,DLedger 会按照日志序号向从节点源源不断的转发日志,从节点接收后将这些待追加的数据放入一个待写队列中。关键中的关键:从节点并不是从挂起队列中处理一个一个的追加请求,而是首先查阅从节点当前已追加的最大日志序号,用 ledgerEndIndex 表示,然后尝试追加 (ledgerEndIndex + 1)的日志,用该序号从代写队列中查找,如果该队列不为空,并且没有 (ledgerEndIndex + 1)的日志条目,说明从节点未接收到这条日志,发生了数据缺失。然后从节点在响应主节点 append 的请求时会告知数据不一致,然后主节点的日志转发线程其状态会变更为COMPARE,将向该从节点发送COMPARE命令,用来比较主从节点的数据差异,根据比较的差异重新从主节点同步数据或删除从节点上多余的数据,最终达到一致。于此同时,主节点也会对PUSH超时推送的消息发起重推,尽最大可能帮助从节点及时更新到主节点的数据。

更多问题,`欢迎大家留言与我一起探讨。如果觉得文章对自己有些用处的话,麻烦帮忙点个赞,谢谢。

推荐阅读:RocketMQ 日志复制系列文章:
1、源码分析 RocketMQ DLedger 多副本存储实现
2、源码分析 RocketMQ DLedger(多副本) 之日志追加流程
3、源码分析 RocketMQ DLedger(多副本) 之日志复制(传播)


原文发布时间为:2019-10-02
本文作者:丁威,《RocketMQ技术内幕》作者。
本文来自中间件兴趣圈,了解相关信息可以关注中间件兴趣圈

相关实践学习
快速体验阿里云云消息队列RocketMQ版
本实验将带您快速体验使用云消息队列RocketMQ版Serverless系列实例进行获取接入点、创建Topic、创建订阅组、收发消息、查看消息轨迹和仪表盘。
消息队列 MNS 入门课程
1、消息队列MNS简介 本节课介绍消息队列的MNS的基础概念 2、消息队列MNS特性 本节课介绍消息队列的MNS的主要特性 3、MNS的最佳实践及场景应用 本节课介绍消息队列的MNS的最佳实践及场景应用案例 4、手把手系列:消息队列MNS实操讲 本节课介绍消息队列的MNS的实际操作演示 5、动手实验:基于MNS,0基础轻松构建 Web Client 本节课带您一起基于MNS,0基础轻松构建 Web Client
目录
相关文章
|
7月前
|
消息中间件 存储 缓存
RocketMQ原理—4.消息读写的性能优化
本文详细解析了RocketMQ消息队列的核心原理与性能优化机制,涵盖Producer消息分发、Broker高并发写入、Consumer拉取消息流程等内容。重点探讨了基于队列的消息分发、Hash有序分发、CommitLog内存写入优化、ConsumeQueue物理存储设计等关键技术点。同时分析了数据丢失场景及解决方案,如同步刷盘与JVM OffHeap缓存分离策略,并总结了写入与读取流程的性能优化方法,为理解和优化分布式消息系统提供了全面指导。
RocketMQ原理—4.消息读写的性能优化
|
7月前
|
消息中间件 存储 设计模式
RocketMQ原理—5.高可用+高并发+高性能架构
本文主要从高可用架构、高并发架构、高性能架构三个方面来介绍RocketMQ的原理。
2455 21
RocketMQ原理—5.高可用+高并发+高性能架构
|
7月前
|
存储 消息中间件 缓存
RocketMQ原理—3.源码设计简单分析下
本文介绍了Producer作为生产者是如何创建出来的、启动时是如何准备好相关资源的、如何从拉取Topic元数据的、如何选择MessageQueue的、与Broker是如何进行网络通信的,Broker收到一条消息后是如何存储的、如何实时更新索引文件的、如何实现同步刷盘以及异步刷盘的、如何清理存储较久的磁盘数据的,Consumer作为消费者是如何创建和启动的、消费者组的多个Consumer会如何分配消息、Consumer会如何从Broker拉取一批消息。
352 11
RocketMQ原理—3.源码设计简单分析下
|
7月前
|
存储 消息中间件 网络协议
RocketMQ原理—1.RocketMQ整体运行原理
本文详细解析了RocketMQ的整体运行原理,涵盖从生产者到消费者的全流程。首先介绍生产者发送消息的机制,包括Topic与MessageQueue的关系及写入策略;接着分析Broker如何通过CommitLog和ConsumeQueue实现消息持久化,并探讨同步与异步刷盘的优缺点。同时,讲解基于DLedger技术的主从同步原理,确保高可用性。消费者部分则重点讨论消费模式(集群 vs 广播)、拉取消息策略及负载均衡机制。网络通信层面,基于Netty的高性能架构通过多线程池分工协作提升并发能力。最后,揭示mmap与PageCache技术优化文件读写的细节,总结了RocketMQ的核心运行机制。
RocketMQ原理—1.RocketMQ整体运行原理
|
7月前
|
消息中间件 Java 数据管理
RocketMQ原理—2.源码设计简单分析上
本文介绍了NameServer的启动脚本、启动时会解析哪些配置、如何初始化Netty网络服务器、如何启动Netty网络服务器,介绍了Broker启动时是如何初始化配置的、BrokerController的创建以及包含的组件、BrokerController的初始化、启动、Broker如何把自己注册到NameServer上、BrokerOuterAPI是如何发送注册请求的,介绍了NameServer如何处理Broker的注册请求、Broker如何发送定时心跳
|
消息中间件 存储 数据库
深入学习RocketMQ的底层存储设计原理
文章深入探讨了RocketMQ的底层存储设计原理,分析了其如何通过将数据和索引映射到内存、异步刷新磁盘以及消息内容的混合存储来实现高性能的读写操作,从而保证了RocketMQ作为一款低延迟消息队列的读写性能。
|
消息中间件 存储 Kafka
RocketMQ 工作原理图解,看这篇就够了!
本文详细解析了 RocketMQ 的核心架构、消息领域模型、关键特性和应用场景,帮助深入理解消息中间件的工作原理。关注【mikechen的互联网架构】,10年+BAT架构经验倾囊相授。
RocketMQ 工作原理图解,看这篇就够了!
|
消息中间件 存储 Kafka
MQ 消息队列核心原理,12 条最全面总结!
本文总结了消息队列的12个核心原理,涵盖消息顺序性、ACK机制、持久化及高可用性等内容。关注【mikechen的互联网架构】,10年+BAT架构经验倾囊相授。
|
消息中间件 负载均衡 API
RocketMQ生产者负载均衡(轮询机制)核心原理
文章深入分析了RocketMQ生产者的负载均衡机制,特别是轮询机制的实现原理,揭示了如何通过`ThreadLocal`技术和消息队列的选播策略来确保消息在多个队列之间均衡发送,以及如何通过灵活的API支持自定义负载均衡策略。
|
消息中间件 存储 负载均衡
RocketMQ消费者消费消息核心原理(含长轮询机制)
这篇文章深入探讨了Apache RocketMQ消息队列中消费者消费消息的核心原理,特别是长轮询机制。文章从消费者和Broker的交互流程出发,详细分析了Push和Pull两种消费模式的内部实现,以及它们是如何通过长轮询机制来优化消息消费的效率。文章还对RocketMQ的消费者启动流程、消息拉取请求的发起、Broker端处理消息拉取请求的流程进行了深入的源码分析,并总结了RocketMQ在设计上的优点,如单一职责化和线程池的使用等。
RocketMQ消费者消费消息核心原理(含长轮询机制)