RocketMQ源码探索之主备同步

RocketMQ是一款开源的分布式消息中间件,在互联网及传统行业中应用广泛。作为分布式系统,RocketMQ通常以集群模式建设,从而为上层应用提供消息生产、消费的负载均衡及消息系统的高可用性。RocketMQ通过Master-Slave间的数据复制实现了数据安全,从而保证了消息数据的高可用性。

上图为典型的RocketMQ 2+2集群架构,这种多Master-Slave集群具备如下几个明显区别于单机模式的功能:

  • 数据复制:主节点基于从节点的数据同步请求,准实时地向从节点推送本地Commit Log中的数据,从而保证两端Commit Log的一致性,进而保证故障发生时消息数据的可用性、可靠性。

  • 高可用:主节点发生故障不可提供服务时,消费端自动切换向从节点,继续消费其中剩余消息,避免消息的不可读状态,发送端则自动切换向集群中其他可用主节点,保证消息能够正常发送。

  • 高性能:多Master同时提供消息发送功能,避免集中写入引发的性能下降;启用Slave节点的消息读取功能,避免Master节点在既写又读场景下的性能下降。

作为运维人员,数据的高可用性是我们系统建设时首先要考虑的问题,那么RocketMQ是如何实现主备节点间数据复制的呢?本文将对主备节点间的数据复制展开分析,以便读者对RocketMQ的数据复制加深了解,为将来工作中RocketMQ集群部署架构选择、模式配置等提供帮助。

RocketMQ主备间数据复制的内部机制及数据流程如下:

从上面复制整体逻辑图可以看到,整个逻辑图包含两个角色(master、slave)、三条数据流(handleHA、offsetReport、dataTransfer)。

角色

1、HAClient(Slave): 负责上报本地offset(同步偏移量)信息到Master端,同时接收Master端传输过来的消息数据,并将数据写入内存、等待落盘结果(同步刷盘)。

2、HAService(Master):负责接收SendMessageThread提交的GroupCommitRequest,并在主从数据同步成功后唤醒处于等待状态的SendMessageThread;负责接收HAClient端的offset报送,并更新本地push2SlaveMaxOffset(已同步偏移量)信息,同时将CommitLog中最近新增的数据传输到HAClient。

数据流

在介绍三条数据流之前,我们需要先了解一下整个HA实现的构成,HA实现由下图的五个类负责完成,他们以后台服务方式持续运行于主从两端。通过这五个服务的有机配合,RocketMQ简洁而完美地解决了HA上数据同步、连接管理、SendMessageThread唤醒等问题。

这五个服务分别是:

HAClient、AcceptSocketService、ReadSocketService、WriteSocketService、GroupTranserService,其中HAClient服务于Slave端,其余四个则服务于Master端。

看完上面的类图,读者可以看到RocketMQ的HA其实非常简洁,下面我将结合这幅类图给大家解释一下HA体系中三条数据流的实现逻辑。在解释每条数据流之前,我会先列出这条数据流上的关键词并加以解释,便于读者理解。

数据流1:handleHA(紫色线)

关键词

GroupCommitRequest:发送线程提交的同步确认请求,内含本次消息发送中的消息内容在CommitLog镜像中偏移量。

GroupTransferService:同步确认请求的处理服务,循环检查当前节点HAService的push2SlaveMaxOffset,并负责唤醒处于等待的发送线程。

push2SlaveMaxOffset:当前主节点已完成主从同步的最大偏移量。

数据流过程

Master收到客户端的putMessage(消息发送)请求后,该请求将被分配给其内部SendMessageThreadPool中的某个线程,由其负责完成消息的接受。SendMessageThread完成消息内容到内存Buffer的写入后,在返回给客户端发送结果前它会完成handleDiskFlush、handleHA的检查。前者负责落盘检查,后者则负责主从数据复制检查(只有主节点的配置中brokerRole=SYNC_MASTER时才会出现复制等待,当brokerRole=ASYNC_MASTER时,handleHA内部直接跳过)。在handleHA中,SendMessageThread向GroupTransferService中提交一个GroupCommitRequest对象,该对象中记录了本次消息内容在CommitLog镜像文件中的Offset(偏移量),GroupTransferService收到GroupCommitRequest后,首先检查当前HAService的push2SlaveMaxOffset(已同步完成的偏移量),如果push2SlaveMaxOffset大于GroupCommitRequest中的Offset则表示主备同步已完成,直接返回OK,否则则挂起SendMessageThread,直到push2SlaveMaxOffset被更新,且更新后的push2SlaveMaxOffset大于GroupCommitRequest中的Offset时。

数据流2:offsetReport(红色线)

关键词

currentReportedOffset:Slave端当前已完成同步的最大偏移量,该偏移量会以定期上报或追加上报的方式上报到Master端,Master端使用它更新本地的push2SlaveMaxOffset。

currentPhyOffset:Slave端当前CommitLog镜像中的最大偏移量,这个偏移量由Slave同步数据写入事件改变。

notifyTransferObject:Master在更新push2SlaveMaxOffset成功后,通过唤醒该对象来唤醒GroupTransferService。

数据流过程

在handleHA中我们已经看到SendMessageThread通常会处于同步等待状态,即等待push2SlaveMaxOffset的更新,直至该偏移量大于GroupCommitRequest中的偏移量,那么push2SlaveMaxOffset由谁负责更新呢?答案是push2SlaveMaxOffset的更新由HAClient的主动上报完成,HAClient的主动上报分成两种:定时上报、追加上报。

1、定时上报

HAClient持续运行在从节点上,Slave端默认每5秒钟(发送间隔可以通haSendHeartbeatInterval修改)主动向Master端发送一次当前currentReportedOffset信息。

2、追加上报

Slave端在处理完同步数据读取、处理后会执行一次追加上报操作,当该操作发现Slave节点的currentPhyOffset大于currentReportedOffset(即CommitLog镜像物理最大偏移量大于已上报偏移量)时,会将currentPhyOffset上报到Master端,同时将currentReportedOffset调整成currentPhyOffset。

3、GroupTransferService唤醒

Master端的ReadSocketService收到Slave端的Offset上报信息,读取该slaveAckOffset,并通过原子操作比较并更新本地的push2SlaveMaxOffset,如果push2SlaveMaxOffset更新成功则通过notifyTransferSome来唤醒GroupTransferService。GroupTransferService被唤醒后开始遍历GroupCommitRequest列表中的所有请求的Offset信息,发现某个请求的Offset小于新更新的push2SlaveMaxOffset则唤醒该请求相关联的SendMessageThread,至此消息的发送才算正式完成。

数据流3:dataTransfer(绿色线)

关键词

nextTransferFromWhere:记录Master端待同步数据的起始偏移量。

readPosition:记录Master端待同步数据的终点偏移量,该偏移量的取值方式跟Broker有没有启用transientPool(异步刷盘且transientPoolEnabled时才会启用)有关,这里不细述。

selectMappedBufferResult:待同步数据buffer,映射到CommitLog镜像中nextTransferFromWhere到readPosition间的数据,WriteSocketService会通过一次或多次写入将其中的数据推送到Slave端。同步数据单次发送最多为32k,单次发送大小可通过haTransferBatchSize来调整。

Master端向slave端推送数据报文机构如下,前8个字节为起始偏移量,后4个字节为本次传送数据大小,报文剩余部分为本次传输内容。

数据流过程

Master端向Slave推送分两种:空载、带载。Salve端收到数据后,首先对数据检查,检查通过后将有效数据追加到CommitLog,完成本次数据同步。

  • 空载

默认情况下,Master端的WriteSocketService会每5秒钟向Slave端推送一次空载报文,报文中仅包含nextTransferFromWhere及bodySize(0),用于同步Master端的下次同步起点和Slave端的当前已报送的偏移量。

  • 带载

Master端的WriteSocketService通过getCommitLogData获取nextTransferFromWhere到readPosition之间的数据,如果数据不为空,则说明有待传输数据。WriteSocketService此时先发送头部信息(包含起始offset、selectMappedBufferResult内容长度)到Slave端,头部写入结束后开始写入消息正文。如果selectMappedBufferResult的长度大于32k,则发送分多次完成,每次发送均包含头部发送、正文发送两部分,头部中的Offset每次均会重新调整。

  • messageAppend

Slave端收到Master端的推送数据后,首先检查Master端发送来的同步起点偏移量是否和当前的slavePhyOffset是否相等,不等则同步出错,这个比较是为了确保每一条消息在Master、Slave中的偏移量完全一致,避免其他数据文件的重检工作。检查通过后,Slave端读取数据内容,并通过appendToCommitLog将同步数据追加到本节点的CommitLog中,至此主备两端的数据同步完成。

  • 追加上报

messageAppend结束后,Slave端的CommitLog最大偏移量出现改变,此时HAClient端会执行一次追加上报以便Master端能及时收到Slave端的最新同步偏移量信息。

总结

本文通过跟踪、分析RocketMQ HA体系实现的三条数据流带着读者分析了一下RocketMQ的主从同步机制,以便读者对此加以了解,便于在将来的RocketMQ集群建设时选择合适的部署架构、复制模式等。RocketMQ的HA体系结构简单、清晰,但内部逻辑相对复杂,有兴趣的读者可以下载RocketMQ的源码进行进一步分析研究,以便获得对RocketMQ的更深理解。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值