RocketMQ 源码探究 -- 主从复制实现
高可用是分布式系统必备的机制之一。RocketMQ 主要通过主从复制的方式来保证消息的高可用。本文主要介绍 RocketMQ 主从复制的核心设计原理。
主从复制简介
当消息到达主服务器后,需将消息同步至从服务器。RocketMQ 支持同步和异步两种复制模式,在生产环境中,为兼顾可靠性和效率,通常采用异步刷盘,同步复制的策略。
当 broker 启动时,会对外暴露 10912
端口用于主从复制。
broker 端口监听
主从复制实现
RocketMQ 中,负责主从复制的类都位于 store 项目的 ha 包下。从功能上分为网络请求处理和复制逻辑处理两部分。
网络
服务端设计
因主从复制逻辑比较简单,且从节点数目有限,故 RocketMQ 没有使用 Netty 为底层网络框架。RocketMQ 通过 NIO 技术实现了 Socket 服务。
主从复制网络实现
HAService 类启动时会调用 AcceptSocketService#beginAccept
方法,完成 ServerSocket 的创建以及初始化工作,然后调用 start 方法开启线程,每隔 1s 监听一次 accept 事件。当监听到 accept 事件时,将 ON_READ
和 ON_WRITE
事件委托给 HAConnection
类。HAConnection 又启动ReadSocketService
和 WriteSocketService
两个线程来分别处理读写事件,通过这种简易 Reactor
模式有效支撑了 client 的请求。
客户端设计
客户端逻辑主要由 HAClient 类实现。当 HAClient 线程被启动后,直接与主节点建立 Socket 连接,每隔 1s 处理一次读事件。
同步复制
主从结点以长链接的形式交互,两者每隔 5s 互相发送心跳包,避免链接断开。
消息复制行为的发起方是 HAClient。从节点维护其 broker 持久化的 offset,最多每隔 5s 向主节点汇报一次,汇报行为有以下两点意义。
同步复制逻辑
主节点触发同步复制逻辑后,将复制的消息的 offset 阻塞在 GroupTransferService#doWaitTransfer
方法上,该方法每隔 1s 检查从节点复制进度与待复制消息两个 offset 的大小关系,当复制进度超过待复制消息 offset 后,返回同步复制成功;否则继续等待,直到超时或者成功。
消息复制请求
主节点的 ReadSocketService
线程读取到从节点的 offset 进度后,将其保存到 slaveRequestOffset
中,随后触发 WriteSocketService
线程进行消息写入的流程。
发送的消息分为 header 和 body 两个部分,header 为 12 字节:8 字节的起始 offset 和 4 字节的消息 size。body 就是自起始位置起 commitlog 的所有数据,最大为 32kb。
从节点获取到主节点返回数据后,首先校验传过来的起始位置是否与传入的 offset 匹配,如果不匹配,则断开与主节点的链接。这意味着与主节点的数据出现不一致,所以要关闭链接。
校验 offset 通过后,直接将 ByteBuffer 中的数据写入本地 Commitlog 中(这里不需要做更多校验,主节点已经做过了)写入成功后再次向主节点汇报进度,以让阻塞在同步复制上的线程快速唤醒。
总结
- RocketMQ 通过主从复制实现消息的高可用。
- RocketMQ 基于标准的 NIO 实现了主从同步的网络层。
- 从节点通过单线程 + 校验起始 offset 来确保主从消息一致性。
转载自:https://juejin.cn/post/7374685303884742708