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

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

本篇主要分析v5的客户端部分源码类

分析源码

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>

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

旧版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();

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

  • 同步发送普通消息
  • 同步发送事务消息
  • 异步发送普通消息

SendReceipt send(Message message) throws ClientException;

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

CompletableFuture<SendReceipt> sendAsync(Message message);

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;
}

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;


}

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);
}

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 实现事务检查的异步执行,避免阻塞主线程,提升客户端处理性能。
        // - 使用 ListeningExecutorService 包装线程池 telemetryCommandExecutor ,提交一个 Callable 任务,调用 checker.check(messageView) 执行具体的事务状态检查逻辑(由业务方实现,返回 TransactionResolution 表示事务最终决议)。
        final ListeningExecutorService service = MoreExecutors.listeningDecorator(telemetryCommandExecutor);
        final Callable<TransactionResolution> task = () -> checker.check(messageView);
        future = service.submit(task);
    } catch (Throwable t) {
   
   
        // - 若提交任务失败(如线程池拒绝),创建一个 SettableFuture 并设置异常,确保后续回调能捕获错误。
        final SettableFuture<TransactionResolution> future0 = SettableFuture.create();
        future0.setException(t);
        future = future0;
    }
    Futures.addCallback(future, new FutureCallback<TransactionResolution>() {
   
   
        @Override
        public void onSuccess(TransactionResolution resolution) {
   
   
            try {
   
   
                if (null == resolution || TransactionResolution.UNKNOWN.equals(resolution)) {
   
   
                    return;
                }
                // 若事务决议 resolution 非 UNKNOWN ,则调用 endTransaction 方法向服务端反馈最终事务状态(提交/回滚),完成事务消息的最终处理。
                final GeneralMessage generalMessage = new General
评论 1
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

scl、

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值