RocketMQ-Producer发送消息源码笔记

170 阅读4分钟

RocketMQ Producer发送消息源码分析

RocketMQ是一款分布式消息中间件,它提供了高性能、低延迟的消息传递服务。在RocketMQ中,Producer(生产者)负责将消息发送到Broker(消息服务器)。发送方式分为三种同步发送、异步发送、单向发送。

一、Producer的启动流程

在发送消息之前,首先需要启动Producer。RocketMQ的Producer接口默认实现为DefaultMQProducer。以下是Producer启动的主要步骤:

  1. 创建Producer实例

    	DefaultMQProducer producer = new DefaultMQProducer(producerGroup);
    	producer.setNamesrvAddr("127.0.0.1:9876");
    
  2. 启动Producer

    	producer.start();
    

    start()方法内部实际上调用了DefaultMQProducerImplstart()方法。启动过程中主要做了以下几件事:

    • 校验GroupName:检查生产者组名是否合法。
    • 设置InstanceName:如果没有设置实例名称,则自动设置为进程PID加上时间戳。
    • 获取MQClientInstance:根据ClientID获取对应的MQClientInstance,如果不存在则创建新的实例。
    • 注册Producer:将当前生产者注册到MQClientInstance中。
    • 启动MQClientInstance:启动Netty客户端,向NameServer拉取Broker信息,并启动下面 5个定时任务
        1. 默认定时2h从NameServer获取地址信息(如果clientConfig中的NamesrvAddr为空)。
        1. 默认定时30s从NameServer更新主题路由信息。
        1. 默认定时30s清理离线Broker信息,并向所有Broker发送心跳。
        1. 默认定时5s持久化所有消费者的偏移量。
        1. 默认定时1min调整线程池大小。

image.png 需要注意事务消息和普通消息实现类不一样,其他都是DefaultMQProducerImpl实现,就是事务消息启动多了一个检查线程的配置checkExecutor是用来检查事务状态的线程池。

image.png image.png

二、消息发送流程

RocketMQ支持三种消息发送方式:同步(sync)、异步(async)和单向(oneway)。以下是这三种发送方式的源码分析:

  1. 同步发送

    	SendResult sendResult = producer.send(msg);
    

    同步发送时,Producer会阻塞当前线程,直到Broker返回发送结果。在DefaultMQProducerImplsendDefaultImpl()方法中,同步发送的主要步骤如下:

    • 计算重发次数:根据配置的重试次数加1,默认重试次数为2,因此总共会发送3次。

    • 循环发送消息:在循环中,选择一个MessageQueue,然后调用sendKernelImpl()方法进行发送(里面处理发送消息前后的hook)。

    • 处理发送结果:如果发送成功,则返回发送结果;如果发送失败,则根据配置决定是否继续重试。

image.png

  1. 异步发送

    	producer.send(msg, new SendCallback() {
    
    	    @Override
    
    	    public void onSuccess(SendResult sendResult) {
    	        // 处理发送成功的结果
    	    }
    	    @Override
    
    	    public void onException(Throwable e) {
    	        // 处理发送异常
    	    }
    
    	});
    

    异步发送时,Producer会构建一个发送任务,并将其提交给线程池。任务执行完成后,会回调用户自定义的回调函数。在sendDefaultImpl()方法中,异步发送的主要步骤与同步发送类似,但在发送完成后,不会阻塞当前线程,而是直接返回。

image.png 异常的回调在上面捕获异常里面回掉了,成功后MQClientAPIImpl会调用 BackpressureSendCallBack再去调用业务处理。 image.png

image.png

  1. 单向发送

    	producer.sendOneway(msg);
    

    单向发送时,Producer只负责发送请求,不等待Broker的响应结果。在sendDefaultImpl()方法中,单向发送的主要步骤与同步和异步发送类似,但在发送完成后,不会等待发送结果,也不会回调用户自定义的回调函数。

    public void sendOneway(Message msg) throws MQClientException, RemotingException, InterruptedException {
        try {
            this.sendDefaultImpl(msg, CommunicationMode.ONEWAY, null, this.defaultMQProducer.getSendMsgTimeout());
        } catch (MQBrokerException e) {
            throw new MQClientException("unknown exception", e);
        }
    }
    

三、消息发送的核心方法

DefaultMQProducerImpl类中,sendKernelImpl()方法是消息发送的核心方法。该方法的主要步骤如下:

  1. 验证消息:检查消息是否合法,包括Topic、Body等。
  2. 查找路由:从NameServer拉取Topic的路由信息,并选择一个MessageQueue。
  3. 选择队列:采用轮询算法选择一个MessageQueue,以保证每个Queue队列的消息投递数量尽可能均匀。
  4. 消息发送:通过Netty客户端将消息发送到Broker。

四、技术亮点

RocketMQ在消息发送过程中采用了多种技术亮点,以提高性能和可靠性:

  • Netty通信:RocketMQ基于Netty实现网络传输,支持异步通信,提高了通信效率。
  • 轮询算法:在选择MessageQueue时采用轮询算法,保证每个Queue队列的消息投递数量尽可能均匀。
  • 重试机制:在消息发送失败时,根据配置的重试次数进行重试,提高了消息发送的可靠性。
  • 故障延迟机制:在消息发送过程中,根据Broker的发送时长和不可用时长进行筛选,避免向不可用的Broker发送消息。