文章目录
发送消息入口DefaultMQProducer
前面说了RocketMQ消息生产者的启动逻辑,现在接着说对应的消息发送的逻辑。消息发送的入口在DefaultMQProducer
类的send
方法。这个方法有很多的重载方法对应的参数各不一样这里举例其中参数最多的方法进行说明
public void send(Collection<Message> msgs, MessageQueue mq, SendCallback sendCallback, long timeout)
方法的参数分别说明一下:
参数 | 类型 | 说明 |
---|---|---|
msgs | Message | 消息对象 |
mq | MessageQueue | 发送指定指定的queue |
sendCallback | SendCallback | 发送回调方法 |
timeout | long | 超时时间 |
消息发送实现DefaultMQProducerImpl
send
方法对应的实现直接委托给了DefaultMQProducerImpl
实现具体的细节。这个类中有两个默认的发送消息的实现,一个是同步的一个是异步的。两者的区别其实很简单,异步相对同步方法来说多了一个自定义回调实现。看一下两者对比
//异步实现
public void send(Message msg,
SendCallback sendCallback) throws MQClientException, RemotingException, InterruptedException {
send(msg, sendCallback, this.defaultMQProducer.getSendMsgTimeout());
}
//同步实现
public SendResult send(
Message msg) throws MQClientException, RemotingException, MQBrokerException, InterruptedException {
//默认消息超时时间3000毫秒,也就是5分钟
return send(msg, this.defaultMQProducer.getSendMsgTimeout());
}
但是两个方法的底层调用的还是同一个方法sendDefaultImpl
。这个方法也是我们要重点分析的对象。先简单说一下方法的入参都是什么。
参数 | 参数类型 | 说明 |
---|---|---|
msg | Message |
消息信息封装对象 |
communicationMode | CommunicationMode |
消息发送类型,同步或者异步 |
sendCallback | SendCallback |
异步发送模式下的回调函数 |
timeout | long |
消息发送超时时间 |
发送消息主方逻辑实现 sendDefaultImpl
这里只把正常逻辑的代码列出来,异常相关的逻辑忽略
private SendResult sendDefaultImpl(
Message msg,
final CommunicationMode communicationMode,
final SendCallback sendCallback,
final long timeout
) throws MQClientException, RemotingException, MQBrokerException, InterruptedException {
//确认客户端的状态
this.makeSureStateOK();
/**
* 消息校验,包含消息的topic校验和消息体的校验。
* topic校验包含以下几点,topic的名字,长度以及是否为不准用的topic(SCHEDULE_TOPIC_XXXX)
* 消息体校验 消息体是不是空和消息体的长度
*/
Validators.checkMessage(msg, this.defaultMQProducer);
//这次调用的id
final long invokeID = random.nextLong();
//开始时间戳
long beginTimestampFirst = System.currentTimeMillis();
long beginTimestampPrev = beginTimestampFirst;
long endTimestamp = beginTimestampFirst;
//从topic配置缓存中查找对应的已经发布的topic,这里可能会去nameServer去拉取配置
TopicPublishInfo topicPublishInfo = this.tryToFindTopicPublishInfo(msg.getTopic());
//配置不为null,并且配置有效
if (topicPublishInfo != null && topicPublishInfo.ok()) {
boolean callTimeout = false;
MessageQueue mq = null;
Exception exception = null;
SendResult sendResult = null;
//交互模式是同步,那么失败重试的次数是默认的配置次数(默认为2)+1,如果是异步失败重试次数是1
int timesTotal = communicationMode == CommunicationMode.SYNC ? 1 + this.defaultMQProducer.getRetryTimesWhenSendFailed() : 1;
int times = 0;
//记录重试时候发送消息目标Broker名字的数组
String[] brokersSent = new String[timesTotal];
//进行重试次数的循环发送消息逻辑
for (; times < timesTotal; times++) {
//获取broker的名字,第一次为null,第二次为上次选择的broker名字
String lastBrokerName = null == mq ? null : mq.getBrokerName();
//根据topic配置选择一个MessageQueue
MessageQueue mqSelected = this.selectOneMessageQueue(topicPublishInfo, lastBrokerName);
//如果
if (mqSelected != null) {
mq = mqSelected;
//添加选择的broker名称到已选集合中
brokersSent[times] = mq.getBrokerName();
try {
beginTimestampPrev = System.currentTimeMillis();
//重试的次数之内,需要设置消息的topic
if (times > 0) {
//Reset topic with namespace during resend.
msg.setTopic(this.defaultMQProducer.withNamespace(msg.getTopic()));
}
long costTime = beginTimestampPrev - beginTimestampFirst;
//这里的timeout,超时时间默认是3000毫秒
if (timeout < costTime) {
callTimeout = true;
break