RocketMQ

273 阅读8分钟

路由中心

NameServer架构设计

  • Broker消息服务器在启动的时候向所有NameServer注册
  • NameServer中的路由表变化不会通知生产者,这样降低了NameServer实现的复杂性,通过消息发送端提供的容错机制来保证消息发送的高可用
  • NameServer集群之间多台NameServer之间互不通信
  • NameServer每隔10s扫描一次Broker,移除处于不激活状态的Broker

NameServer配置

public class NamesrvConfig {
    // rocketmq主目录,可以通过-Drocketmq.home.dir=path设置
    private String rocketmqHome = System.getProperty(MixAll.ROCKETMQ_HOME_PROPERTY, System.getenv(MixAll.ROCKETMQ_HOME_ENV));
    // NameServer存储KV配置属性的持久化路径
    private String kvConfigPath = System.getProperty("user.home") + File.separator + "namesrv" + File.separator + "kvConfig.json";
    // 默认配置文件路径,不生效
    private String configStorePath = System.getProperty("user.home") + File.separator + "namesrv" + File.separator + "namesrv.properties";
    private String productEnvName = "center";
    private boolean clusterTest = false;
    // 是否支持顺序消费,默认不支持
    private boolean orderMessageEnable = false;
}

NettyServer配置

public class NettyServerConfig implements Cloneable {
    // NameServer监听端口,该值默认会被初始化为9876
    private int listenPort = 8888;
    // Netty业务线程池个数
    private int serverWorkerThreads = 8;
    // Netty public任务线程池个数
    private int serverCallbackExecutorThreads = 0;
    // IO线程池个数,主要是NameServer、Broker端解析请求、返回相应的线程个数。这类线程主要是处理网络请求,解析请求包,
    然后转发到各个业务线程池完成具体的业务操作,然后将结果再返回给调用方
    private int serverSelectorThreads = 3;
    // send oneway消息请求并发度(Broker端参数)
    private int serverOnewaySemaphoreValue = 256;
    // 异步消息发送最大并发度(Broker端参数)
    private int serverAsyncSemaphoreValue = 64;
    // 网络连接最大空闲时间,如果空闲时间超过该值,连接将被关闭
    private int serverChannelMaxIdleTimeSeconds = 120;
    // 网络Socket发送缓存区大小,默认64K
    private int serverSocketSndBufSize = NettySystemConfig.socketSndbufSize;
    // 网络Socket接收缓存区大小,默认64K
    private int serverSocketRcvBufSize = NettySystemConfig.socketRcvbufSize;
    // ByteBuffer是否开启缓存
    private boolean serverPooledByteBufAllocatorEnable = true;
    // 是否启用Epoll
    private boolean useEpollNativeSelector = false;
    }

路由元信息

路由元信息就是NameServer存储的信息

public class RouteInfoManager {
    private final HashMap<String/* topic */, List<QueueData>> topicQueueTable;
    private final HashMap<String/* brokerName */, BrokerData> brokerAddrTable;
    private final HashMap<String/* clusterName */, Set<String/* brokerName */>> clusterAddrTable;
    private final HashMap<String/* brokerAddr */, BrokerLiveInfo> brokerLiveTable;
    private final HashMap<String/* brokerAddr */, List<String>/* Filter Server */> filterServerTable;
}
  • topicQueueTable:Topic消息队列路由消息,消息发送时根据路由表进行负载均衡
  • brokerAddrTable:Broker基本信息,包括brokerName、所属集群名字、主备Broker地址
  • clusterAddrTable:集群中所有Broker名称
  • brokerLiveTable:Broker状态信息
  • filterServerTable:Broker上的FilterServer列表,用于类模式消息过滤 RocketMQ基于订阅发布机制,一个Topic拥有多个消息队列,一个Broker为每一个主题默认创建4个读队列4个写队列。多个Broker组成一个集群,BrokerName由相同的多台Broker组成Master-Slave架构,brokerId为0代表Master,大于0表示Slave。BrokerLiveInfo中的lastUpdateTimestamp存储上次收到Broker心跳包的时间

路由注册

RocketMQ路由注册是通过Broker与NameServer的心跳功能实现的。Broker启动时向集群中所有的NameServer发送心跳语句,每隔30s向集群中所有NameServer发送心跳包,NameServer收到Broker心跳包时会更新brokerLiveTable缓存中BrokerLiveInfo的lastUpdateTimestamp,然后NameServer每隔10s扫描brokerLiveTable,如果连接120s没有收到心跳包,NameServer将移除该Broker的路由信息同时关闭Socket连接

心跳包

  • brokerAddr
  • brokerId:0为master,大于0为slave
  • brokerName
  • clusterName
  • haServerAddr:master地址,初次请求时该值为空,slave向NameServer注册后返回
  • requestBody 设计亮点:路由表使用了锁粒度较少的读写锁,允许多个Producer并发读,保证消息发送时的高并发。但同一时刻NameServer只处理一个Broker心跳包,多个心跳包请求串行执行

路由删除

NameServer收到Broker心跳包时会更新brokerLiveTable缓存中BrokerLiveInfo的lastUpdateTimestamp,然后NameServer每隔10s扫描brokerLiveTable,如果连接120s没有收到心跳包,NameServer将移除该Broker的路由信息同时关闭Socket连接

路由发现

RocketMQ路由发现是非实时的,当Topic路由出现变化后,NameServer不主动推送给客户端,而是由客户端定时拉取主题最新的路由

消息发送

RocketMQ支持三种消息发送方式:同步、异步、单向

同步:发送者向MQ执行发送消息API时,同步等待,直到消息服务器返回发送结果

异步:发送消息时,生产者指定消息发送成功后的回调函数,然后调用发送API后,立即返回,消息发送者线程不阻塞,直到线程运行结束,消息发送成功或失败的回调任务在一个新的线程执行。

单向:发送消息时,直接返回,不等待消息服务器的结果,也不注册回调函数。

消息体

生产者启动流程

消息发送基本流程

消息长度验证

发送消息的最大长度4M(maxMessageSize=1024 *1024 *4)

查找主题路由信息

如果生产者中缓存了topic的路由信息,如果该路由信息中包含了消息队列,则直接返回该路由信息,如果没有缓存或没有包含消息队列,则向NameServer查询该topic的路由消息。如果最终未找到路由信息,则抛出异常

选择消息队列

选择消息队列有两种方式

  1. sendLatencyFaultEnable=false 默认不启用Broker故障延迟机制
  2. sendLatencyFaultEnable=true 启用Broker故障延迟机制 消息生产者每隔30s更新一次路由信息

什么是Broker故障延迟机制

如果不采用故障延迟机制,那么在broker出现异常时,生产者在重试时会不断的访问失效的队列。采用故障延迟机制后发送消息的步骤如下:

  1. 在消息发送失败,mq根据消息发送耗时来预测该broker不可用的时长,并将broker名称,及”预计恢复时长“,存储于ConcurrentHashMap<String, FaultItem> faultItemTable中
  2. 在开启消息容错后,选择消息队列时,会根据当前时间与FaultItem中该broker的预计恢复时间做比较,若(System.currentTimeMillis() - startTimestamp) >= 0,则预计该broker恢复正常,选择该broker的消息队列
  3. 若所有的broker都预计不可用,随机选择一个不可用的broker,从路由信息中选择下一个消息队列,重置其brokerName,queueId,进行消息发送

消息发送

  1. 根据MessageQueue获取Broker的网络地址
  2. 为消息分配全局唯一ID
  3. 如果注册了消息发送钩子函数,则执行消息发送之前的增强逻辑
  4. 构建消息发送包
  5. 根据消息发送方式,同步、异步、单向方式进行网络传输

批量消息发送

批量消息发送是将同一主题的多条消息一起打包发送到消息服务端,减少网络调用次数,提高网络传输效率。单批次消息发送总长度不能超过DefaultMQProducer#maxMessageSize=4M

消息存储

消息存储设计原理图.png CommitLog:消息存储文件,所有消息主题的消息都存储在CommitLog文件中

CommitLog文件存储目录为${ROCKET_HOME}/store/commitlog目录,每个文件默认1G。第一个文件的初始偏移量为0,
第二个文件1073741824,代表该文件第一条消息的物理偏移量为1073741824,这样根据物理偏移量快速定位到消息。
MappedFileQueue可以看做${ROCKET_HOME}/store/commitlog文件夹,MappedFile看作该文件夹下的一个文件
broker配置文件 storePathRootDir修改路径
mapedFileSizeCommitLog修改文件大小

ConsumeQueue:消息消费队列,消息到达CommitLog文件后,将异步转发到消息消费队列,供消息消费者消费

IndexFile:消息索引文件,主要存储消息Key与Offset的对应关系

abort:如果存在该文件说明Broker非正常关闭,该文件默认启动时创建,正常退出之前删除

checkpoint:文件检测点,存储commitLog文件最后一次刷盘时间戳、consumequeue最后一次刷盘时间、index索引文件最后一次刷盘时间戳

事务状态服务:存储每条消息的事务状态

定时消息服务:每一个延迟级别对应一个消息消费队列,存储延迟队列的消息拉取进度

消息发送存储流程

存储文件组织与内存映射

对于commitlog、consumequeue、index三类大文件进行磁盘读写操作,均是通过MapedFile类来完成

RocketMQ使用MappedFile、MappedFileQueue来封装存储文件

RocketMQ存储文件

存储文件.png ConsumeQueue 由于不同topic的消息可能存在相同的commitLog中,那么也就是说同一topic的数据在commitLog中也可能是不连续的,那么对于数据的查找非常低效,ConsumeQueue就是为了满足消费的检索需求

单个ConsumeQueue文件中默认包含30w个条目,单个文件的长度为30w*20字节

Index索引文件 为了根据key找到消息数据的offset,快速完成数据检索

checkpoint文件 checkpoint的作用是记录CommitLog、ConsumeQueue、Index文件的刷盘时间点,文件固定长度为4K,其中只用该文件的前面24个字节

实时更新消息消费队列与索引文件

当生产者提交消息到commitLog时,Broker通过开启一个线程ReputMessageService来实时转发CommitLog文件更新事件,及时更新ConsumeQueue、IndexFile文件

消息队列与索引文件恢复

文件刷盘机制

过期文件删除机制

消息消费

消息过滤FilterServer

主从同步

事务消息