🚀 优质资源分享 🚀
学习路线指引(点击解锁) | 知识定位 | 人群定位 |
---|---|---|
🧡 Python实战微信订餐小程序 🧡 | 进阶级 | 本课程是python flask+微信小程序的完美结合,从项目搭建到腾讯云部署上线,打造一个全栈订餐系统。 |
💛Python量化交易实战💛 | 入门级 | 手把手带你打造一个易扩展、更安全、效率更高的量化交易系统 |
消息发送
首先来看一个RcoketMQ发送消息的例子:
@Service
public class MQService {
@Autowired
DefaultMQProducer defaultMQProducer;
public void sendMsg() {
String msg = "我是一条消息";
// 创建消息,指定TOPIC、TAG和消息内容
Message sendMsg = new Message("TestTopic", "TestTag", msg.getBytes());
SendResult sendResult = null;
try {
// 同步发送消息
sendResult = defaultMQProducer.send(sendMsg);
System.out.println("消息发送响应:" + sendResult.toString());
} catch (MQClientException e) {
e.printStackTrace();
} catch (RemotingException e) {
e.printStackTrace();
} catch (MQBrokerException e) {
e.printStackTrace();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
RocketMQ是通过DefaultMQProducer
进行消息发送的,它实现了MQProducer
接口,MQProducer
接口中定义了消息发送的方法,方法主要分为三大类:
- 同步进行消息发送,向Broker发送消息之后等待响应结果
- 异步进行消息发送,向Broker发送消息之后立刻返回,当消息发送完毕之后触发回调函数
- sendOneway单向发送,也是异步消息发送,向Broker发送消息之后立刻返回,但是没有回调函数
public interface MQProducer extends MQAdmin {
// 同步发送消息
SendResult send(final Message msg) throws MQClientException, RemotingException, MQBrokerException,
InterruptedException;
// 异步发送消息,SendCallback为回调函数
void send(final Message msg, final SendCallback sendCallback) throws MQClientException,
RemotingException, InterruptedException;
// 异步发送消息,没有回调函数
void sendOneway(final Message msg) throws MQClientException, RemotingException,
InterruptedException;
// 省略其他方法
}
接下来以将以同步消息发送为例来分析消息发送的流程。
DefaultMQProducer
里面有一个DefaultMQProducerImpl
类型的成员变量defaultMQProducerImpl
,从默认的无参构造函数中可以看出在构造函数中对defaultMQProducerImpl
进行了实例化,在send
方法中就是调用defaultMQProducerImpl
的方法进行消息发送的:
public class DefaultMQProducer extends ClientConfig implements MQProducer {
/**
* 默认消息生产者实现类
*/
protected final transient DefaultMQProducerImpl defaultMQProducerImpl;
/**
* 默认的构造函数
*/
public DefaultMQProducer() {
this(null, MixAll.DEFAULT_PRODUCER_GROUP, null);
}
/**
* 构造函数
*/
public DefaultMQProducer(final String namespace, final String producerGroup, RPCHook rpcHook) {
this.namespace = namespace;
this.producerGroup = producerGroup;
// 实例化
defaultMQProducerImpl = new DefaultMQProducerImpl(this, rpcHook);
}
/**
* 同步发送消息
*/
@Override
public SendResult send(
Message msg) throws MQClientException, RemotingException, MQBrokerException, InterruptedException {
// 设置主题
msg.setTopic(withNamespace(msg.getTopic()));
// 发送消息
return this.defaultMQProducerImpl.send(msg);
}
}
DefaultMQProducerImpl
中消息的发送在sendDefaultImpl
方法中实现,处理逻辑如下:
- 根据设置的主题查找对应的路由信息
TopicPublishInfo
- 获取失败重试次数,在消息发送失败时进行重试
- 获取上一次选择的消息队列所在的Broker,如果上次选择的Broker为空则为NULL,然后调用
selectOneMessageQueue
方法选择一个消息队列,并记录本次选择的消息队列,在下一次发送消息时选择队列时使用 - 计算选择消息队列的耗时,如果大于超时时间,终止本次发送
- 调用
sendKernelImpl
方法进行消息发送 - 调用
updateFaultItem
记录向Broker发送消息的耗时,在开启故障延迟处理机制时使用
public class DefaultMQProducerImpl implements MQProducerInner {
/**
* DEFAULT SYNC -------------------------------------------------------
*/
public SendResult send(
Message msg) throws MQClientException, RemotingException, MQBrokerException, InterruptedException {
// 发送消息
return send(msg, this.defaultMQProducer.getSendMsgTimeout());
}
public SendResult send(Message msg,
long timeout) throws MQClientException, RemotingException, MQBrokerException, InterruptedException {
// 发送消息
return this.sendDefaultImpl(msg, CommunicationMode.SYNC, null, timeout);
}
/**
* 发送消息
* @param msg 发送的消息
* @param communicationMode
* @param sendCallback 回调函数
* @param timeout 超时时间
*/
private SendResult sendDefaultImpl(
Message msg,
final CommunicationMode communicationMode,
final SendCallback sendCallback,
final long timeout
) throws MQClientException, RemotingException, MQBrokerException, InterruptedException {
this.makeSureStateOK();
Validators.checkMessage(msg, this.defaultMQProducer);
final long invokeID = random.nextLong();
// 开始时间
long beginTimestampFirst = System.currentTimeMillis();
long beginTimestampPrev = beginTimestampFirst;
long endTimestamp = beginTimestampFirst;
// 查找主题路由信息
TopicPublishInfo topicPublishInfo = this.tryToFindTopicPublishInfo(msg.getTopic());
if (topicPublishInfo != null && topicPublishInfo.ok()) {
boolean callTimeout = false;
// 消息队列
MessageQueue mq = null;
Exception exception = null;
SendResult sendResult = null;
// 获取失败重试次数
int timesTotal = communicationMode == CommunicationMode.SYNC ? 1 + this.defaultMQProducer.getRetryTimesWhenSendFailed() : 1;
int times = 0;
String[] brokersSent = new String[timesTotal];
for (; times < timesTotal; times++) {
// 获取BrokerName
String lastBrokerName = null == mq ? null : mq.getBrokerName();
// 根据BrokerName选择一个消息队列
MessageQueue mqSelected = this.selectOneMessageQueue(topicPublishInfo, lastBrokerName);
if (mqSelected != null) {
// 记录本次选择的消息队列
mq = mqSelected;
brokersSent[times] = mq.getBrokerName();
try {
// 记录时间
beginTimestampPrev = System.currentTimeMillis();
if (times > 0) {
//Reset topic with namespace during resend.
msg.setTopic(this.defaultMQProducer.withNamespace(msg.getTopic()));
}
// 计算选择消息队列的耗时时间
long costTime = beginTimestampPrev - beginTimestampFirst;
// 如果已经超时,终止发送
if (timeout < costTime) {
callTimeout = true;
break;
}
// 发送消息
sendResult = this.sendKernelImpl(msg, mq, communicationMode, sendCallback, topicPublishInfo, timeout - costTime);
// 结束时间
endTimestamp = System.currentTimeMillis();
// 记录向Broker发送消息的请求耗时,消息发送结束时间 - 开始时间
this.updateFaultItem(mq.getBrokerName(), endTimestamp - beginTimestampPrev, false);
switch (communicationMode) {
case ASYNC:
return null;
case ONEWAY:
return null;
case SYNC:
// 如果发送失败
if (sendResult.getSendStatus() != SendStatus.SEND_OK) {
// 是否重试
if (this.defaultMQProducer.isRetryAnotherBrokerWhenNotStoreOK()) {