RocketMQ Producer发送消息源码分析
RocketMQ是一款分布式消息中间件,它提供了高性能、低延迟的消息传递服务。在RocketMQ中,Producer(生产者)负责将消息发送到Broker(消息服务器)。发送方式分为三种同步发送、异步发送、单向发送。
一、Producer的启动流程
在发送消息之前,首先需要启动Producer。RocketMQ的Producer接口默认实现为DefaultMQProducer。以下是Producer启动的主要步骤:
-
创建Producer实例:
DefaultMQProducer producer = new DefaultMQProducer(producerGroup); producer.setNamesrvAddr("127.0.0.1:9876"); -
启动Producer:
producer.start();start()方法内部实际上调用了DefaultMQProducerImpl的start()方法。启动过程中主要做了以下几件事:- 校验GroupName:检查生产者组名是否合法。
- 设置InstanceName:如果没有设置实例名称,则自动设置为进程PID加上时间戳。
- 获取MQClientInstance:根据ClientID获取对应的MQClientInstance,如果不存在则创建新的实例。
- 注册Producer:将当前生产者注册到MQClientInstance中。
- 启动MQClientInstance:启动Netty客户端,向NameServer拉取Broker信息,并启动下面 5个定时任务
-
- 默认定时2h从NameServer获取地址信息(如果clientConfig中的NamesrvAddr为空)。
-
- 默认定时30s从NameServer更新主题路由信息。
-
- 默认定时30s清理离线Broker信息,并向所有Broker发送心跳。
-
- 默认定时5s持久化所有消费者的偏移量。
-
- 默认定时1min调整线程池大小。
-
需要注意事务消息和普通消息实现类不一样,其他都是
DefaultMQProducerImpl实现,就是事务消息启动多了一个检查线程的配置checkExecutor是用来检查事务状态的线程池。
二、消息发送流程
RocketMQ支持三种消息发送方式:同步(sync)、异步(async)和单向(oneway)。以下是这三种发送方式的源码分析:
-
同步发送:
SendResult sendResult = producer.send(msg);同步发送时,Producer会阻塞当前线程,直到Broker返回发送结果。在
DefaultMQProducerImpl的sendDefaultImpl()方法中,同步发送的主要步骤如下:-
计算重发次数:根据配置的重试次数加1,默认重试次数为2,因此总共会发送3次。
-
循环发送消息:在循环中,选择一个MessageQueue,然后调用
sendKernelImpl()方法进行发送(里面处理发送消息前后的hook)。 -
处理发送结果:如果发送成功,则返回发送结果;如果发送失败,则根据配置决定是否继续重试。
-
-
异步发送:
producer.send(msg, new SendCallback() { @Override public void onSuccess(SendResult sendResult) { // 处理发送成功的结果 } @Override public void onException(Throwable e) { // 处理发送异常 } });异步发送时,Producer会构建一个发送任务,并将其提交给线程池。任务执行完成后,会回调用户自定义的回调函数。在
sendDefaultImpl()方法中,异步发送的主要步骤与同步发送类似,但在发送完成后,不会阻塞当前线程,而是直接返回。
异常的回调在上面捕获异常里面回掉了,成功后
MQClientAPIImpl会调用 BackpressureSendCallBack再去调用业务处理。
-
单向发送:
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()方法是消息发送的核心方法。该方法的主要步骤如下:
- 验证消息:检查消息是否合法,包括Topic、Body等。
- 查找路由:从NameServer拉取Topic的路由信息,并选择一个MessageQueue。
- 选择队列:采用轮询算法选择一个MessageQueue,以保证每个Queue队列的消息投递数量尽可能均匀。
- 消息发送:通过Netty客户端将消息发送到Broker。
四、技术亮点
RocketMQ在消息发送过程中采用了多种技术亮点,以提高性能和可靠性:
- Netty通信:RocketMQ基于Netty实现网络传输,支持异步通信,提高了通信效率。
- 轮询算法:在选择MessageQueue时采用轮询算法,保证每个Queue队列的消息投递数量尽可能均匀。
- 重试机制:在消息发送失败时,根据配置的重试次数进行重试,提高了消息发送的可靠性。
- 故障延迟机制:在消息发送过程中,根据Broker的发送时长和不可用时长进行筛选,避免向不可用的Broker发送消息。