从 消息发送 (Producer发送消息到Broker)、 消息存储 (Broker落盘逻辑)、 消息消费 (Consumer拉取/推送消息)三个核心流程切入分析RocketMQ的客户端、服务端源码。

分析源码

rocketmq: https://github.com/apache/rocketmq.git

client: https://github.com/apache/rocketmq-clients/blob/master/README-CN.md

rocketmq-spring

<!--add dependency in pom.xml-->
<dependency>
    <groupId>org.apache.rocketmq</groupId>
    <artifactId>rocketmq-spring-boot-starter</artifactId>
    <version>${RELEASE.VERSION}</version>
</dependency>
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.

使用的starter就是对rocketmq-spring包装了一层, https://rocketmq.apache.org/zh/docs/sdk/01overview/

RocketMQ源码分析1-客户端消息发送、拉取路由信息、客户端生成消息ID_服务端

旧版RocketMQ的client源码不太一样

  • 如DefaultMQProducer 是旧版 RocketMQ Java 客户端的生产者抽象,实现是DefaultMQProducerImpl,采用传统的面向对象编程方式基于remotting协议,配置和使用相对固定。
  • Producer是java新客户端的抽象,ProducerImpl 是新客户端的生产者实现,基于gRPC协议运用构建器模式进行初始化。

消息发送流程主要的类

这里分析基于gRPC之后的client源码。其中client和Broker通信发送消息的入口类RocketMQClientTemplate。

RocketMQClientTemplate类

RocketMQClientTemplate 是 RocketMQ 的 Spring 客户端模板类,继承 AbstractMessageSendingTemplate (Spring 消息发送抽象类),实现 DisposableBean (用于资源释放),核心功能是封装消息的 同步/异步发送 、 事务消息 、 消息接收与确认 等操作。

同步发送的核心入口是 syncSendGrpcMessage ,被以下方法调用:

  • syncSendDelayMessage (延迟消息)
  • syncSendFifoMessage (FIFO 顺序消息)
  • syncSendNormalMessage (普通消息)

异步发送的核心入口是 asyncSend ,被以下方法调用:

  • asyncSendWithObjectPayload (对象载荷)
  • asyncSendWithStringPayload (字符串载荷)
  • asyncSendWithBytePayload (字节数组载荷)
  • asyncSendNormalMessage (普通异步消息)
  • asyncSendFifoMessage (FIFO 异步消息)
  • asyncSendDelayMessage (延迟异步消息)

主要的成员属性

// 构建Producer实例,producerBuilder.build()
// 可以设置topics、maxAttemps、setTransactionChecker、clientConfiguration等
private ProducerBuilder producerBuilder;
// 构建Consumer实例,consumerBuilder.build()
private SimpleConsumerBuilder simpleConsumerBuilder;

// 生产者抽象接口,指的是和Broker通信的生产者
private volatile Producer producer;
// 消费者抽象接口,指的是和Broker通信的消费者
private volatile SimpleConsumer simpleConsumer;

// 序列化器,支持MappingJackson2MessageConverter、MappingFastJsonMessageConverter、StringMessageConverter、ByteArrayMessageConverter
private RocketMQMessageConverter rocketMQMessageConverter = new RocketMQMessageConverter();
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.

其中Producer是接口,生产者的抽象,包含三个send方法,对应三种将消息发送到Broker的类型

  • 同步发送普通消息
  • 同步发送事务消息
  • 异步发送普通消息
SendReceipt send(Message message) throws ClientException;

SendReceipt send(Message message, Transaction transaction) throws ClientException;

CompletableFuture<SendReceipt> sendAsync(Message message);
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.

ProducerBuilder的实现类ProducerBuilderImpl创建ProducerImpl实例,然后调用startAsync异步启动ProducerImpl,并等待处于可用状态。

// ProducerBuilderImpl
@Override
public Producer build() {
    checkNotNull(clientConfiguration, "clientConfiguration has not been set yet");
    final ProducerImpl producer = new ProducerImpl(clientConfiguration, topics, maxAttempts, checker);
    producer.startAsync().awaitRunning();
    return producer;
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
Producer以及Client类

ProducerImpl 类实现了 RocketMQ 生产者的核心功能,包括启动和关闭生产者、处理孤儿事务消息、发送消息等。通过继承 ClientImpl 类和实现 Producer 接口,该类提供了一个完整的生产者实现,支持同步和异步消息发送,以及事务消息处理。

ProducerImpl类继承ClientImpl类实现Producer接口,在ClientImpl类进行client、clientManager启动,然后做一些准备工作

  • 启动ClientManager,ClientManager 提供一系列统一的 RPC 调用接口,覆盖消息发送、路由查询、心跳、消息确认、将消息发送到DLQ等核心操作。
  • 从NameScvr拉取Broker的topic等信息
  • 注册定时任务,定时30s从Broker更新topic等路由信息。路由信息被放入一个ConcurrentHashMap,key为topic,value为TopicRouteData。

ProducerImpl的属性,主要是记录一些发送消息的配置以及路由数据等

class ProducerImpl extends ClientImpl implements Producer {

    //发布消息的配置信息。
    protected final PublishingSettings publishingSettings;
    // 一个并发映射,用于缓存主题的发布路由数据。
    final ConcurrentMap<String/* topic */, PublishingLoadBalancer> publishingRouteDataCache;
     // 事务检查器,用于处理事务消息。
    private final TransactionChecker checker;


}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.

ProducerImpl实际启动调用ClientImpl的startUp方法

/**
 * Start the rocketmq client and do some preparatory work.
 */
@Override
protected void startUp() throws Exception {

    log.info("Begin to start the rocketmq client, clientId={}", clientId);
    this.clientManager.startAsync().awaitRunning();

    // Fetch topic route from remote.
    log.info("Begin to fetch topic(s) route data from remote during client startup, clientId={}, topics={}",
        clientId, topics);
    for (String topic : topics) {
        final ListenableFuture<TopicRouteData> future = fetchTopicRoute(topic);
        future.get();
    }

    log.info("Fetch topic route data from remote successfully during startup, clientId={}, topics={}",
        clientId, topics);

    // Update route cache periodically.
    final ScheduledExecutorService scheduler = clientManager.getScheduler();

    this.updateRouteCacheFuture = scheduler.scheduleWithFixedDelay(() -> {
        try {
            updateRouteCache();
        } catch (Throwable t) {
            log.error("Exception raised while updating topic route cache, clientId={}", clientId, t);
        }
    }, 10, 30, TimeUnit.SECONDS);
    log.info("The rocketmq client starts successfully, clientId={}", clientId);
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.
  • 28.
  • 29.
  • 30.
  • 31.
  • 32.

ProducerImpl中还有对孤儿事务(如发送事务消息中,客户端党机,此时Broker还存留半事务未结束)的处理,该方法是 RocketMQ 客户端中用于处理 孤儿事务消息恢复 的核心逻辑,主要在服务端检测到未决事务(未提交/回滚的事务)时被调用,触发客户端对事务状态的检查和最终处理。

@Override
public void onRecoverOrphanedTransactionCommand(Endpoints endpoints, RecoverOrphanedTransactionCommand command) {
    // 从 RecoverOrphanedTransactionCommand 命令中提取 transactionId (事务ID)和 messageId (消息ID),用于标识需要处理的事务消息。
    final String transactionId = command.getTransactionId();
    final String messageId = command.getMessage().getSystemProperties().getMessageId();
    
    // 检查事务检查器 :
    // 若 checker (事务检查器,由业务方实现)未注册( null == checker ),直接记录错误日志并返回,避免后续无效操作。
    if (null == checker) {
        log.error("No transaction checker registered, ignore it, messageId={}, transactionId={}, endpoints={},"
                + " clientId={}", messageId, transactionId, endpoints, clientId);
        return;
    }
    MessageViewImpl messageView;
    try {
        // 将服务端传递的 Protobuf 格式消息解码为客户端可识别的 MessageViewImpl 对象。若解码失败(捕获 Throwable ),记录错误日志并终止流程。
        messageView = MessageViewImpl.fromProtobuf(command.getMessage());
    } catch (Throwable t) {
        log.error("[Bug] Failed to decode message during orphaned transaction message recovery, messageId={}, "
                + "transactionId={}, endpoints={}, clientId={}", messageId, transactionId, endpoints, clientId, t);
        return;
    }
    ListenableFuture<TransactionResolution> future;
    try {
        // 通过 ListenableFuture 实现事务检查的异步执行,避免阻塞主线程,提升客户端处理性能。
        // - 使用 ListeningE