-
消息发送基本流程
-
总结
在介绍消息发送流程之前,我们先来看下RocketMQ
的架构图,如下所示:
架构图中的各个模块的大致作用在前几篇文章中已经介绍过了,这里不再进行赘述。主要通过架构图让大家对RocketMQ
中的消息发送有个整体的理解。
消息的发送方式主要包括三种方式:
(1)同步方式;
(2)异步方式:
(3)Oneway
方式;
消息的发送流程主要步骤为:消息验证、路由信息查找以及消息发送三个基本步骤。在DefaultMQProducer
中可以查看到对应的消息发送方法,如下所示:
@Override
public SendResult send(
Message msg) throws MQClientException, RemotingException, MQBrokerException, InterruptedException {
return this.defaultMQProducerImpl.send(msg);
}
…
@Override
public SendResult send(Message msg,
long timeout) throws MQClientException, RemotingException, MQBrokerException, InterruptedException {
return this.defaultMQProducerImpl.send(msg, timeout);
}
…
@Override
public void send(Message msg,
SendCallback sendCallback) throws MQClientException, RemotingException, InterruptedException {
this.defaultMQProducerImpl.send(msg, sendCallback);
}
大致的调用方式如下图所示:
消息发动的方式默认采用同步的方式进行,同时默认的超时时间为3s。在发送消息时,我们首先需要知道该消息要发送到哪里,这就像我们寄快递,需要先明确收件人是谁。那么在RocketMQ
发送消息前会对消息进行基本的消息验证。确认消息是否符合发送要求。
Validators.checkMessage(msg, this.defaultMQProducer);
具体的代码如下所示:
public static void checkMessage(Message msg, DefaultMQProducer defaultMQProducer)
throws MQClientException {
//消息不能为空
if (null == msg) {
throw new MQClientException(ResponseCode.MESSAGE_ILLEGAL, “the message is null”);
}
// 主题不能为空
Validators.checkTopic(msg.getTopic());
// 消息的body不能为空
if (null == msg.getBody()) {
throw new MQClientException(ResponseCode.MESSAGE_ILLEGAL, “the message body is null”);
}
// 消息的body长度不能为0
if (0 == msg.getBody().length) {
throw new MQClientException(ResponseCode.MESSAGE_ILLEGAL, “the message body length is zero”);
}
// 消息的body长度不能唱过最大长度4M
if (msg.getBody().length > defaultMQProducer.getMaxMessageSize()) {
throw new MQClientException(ResponseCode.MESSAGE_ILLEGAL,
"the message body size over max value, MAX: " + defaultMQProducer.getMaxMessageSize());
}
}
消息的校验没有问题则会调用tryToFindTopicPublishInfo(msg.getTopic())
方法。我们需要获取主题的路由信息,通过路由信息我们才知道消息需要被投递到哪个具体的Broker
节点之上。
我们来一起看下查找主题的路由信息方法,如下所示:
private TopicPublishInfo tryToFindTopicPublishInfo(final String topic) {
//先从本地缓存变量topicPublishInfoTable中先get一次
TopicPublishInfo topicPublishInfo = this.topicPublishInfoTable.get(topic);
if (null == topicPublishInfo || !topicPublishInfo.ok()) {
this.topicPublishInfoTable.putIfAbsent(topic, new TopicPublishInfo());
//然后从nameServer上更新topic路由信息
this.mQClientFactory.updateTopicRouteInfoFromNameServer(topic);
//然后再从本地缓存变量topicPublishInfoTable中再get一次
topicPublishInfo = this.topicPublishInfoTable.get(topic);
}
if (topicPublishInfo.isHaveTopicRouterInfo() || topicPublishInfo.ok()) {
return topicPublishInfo;
} else {
//第一次的时候isDefault为false,第二次的时候default为true,即为用默认的topic的参数进行更新
this.mQClientFactory.updateTopicRouteInfoFromNameServer(topic, true, this.defaultMQProducer);
topicPublishInfo = this.topicPublishInfoTable.get(topic);
return topicPublishInfo;
}
}
在生产者中如果缓存了topic
路由信息,路由信息中如果包含了消息队列,那么会进行路由信息的返回。如果没有缓存或者没有队列信息,那么就会向NameServer
查询topic
的路由信息。TopicPublishInfo
属性如下所示:
public class TopicPublishInfo {
//是否是顺序消息
private boolean orderTopic = false;
//是否包含主题路由信息
private boolean haveTopicRouterInfo = false;
//主题队列的消息队列
private List messageQueueList = new ArrayList();
// 没选择一次消息队列,值自增1
private volatile ThreadLocalIndex sendWhichQueue = new ThreadLocalIndex();
private TopicRouteData topicRouteData;
public boolean isOrderTopic() {
return orderTopic;
}
public void setOrderTopic(boolean orderTopic) {
this.orderTopic = orderTopic;
}
public boolean ok() {
return null != this.messageQueueList && !this.messageQueueList.isEmpty();
}
最后
Java架构学习技术内容包含有:Spring,Dubbo,MyBatis, RPC, 源码分析,高并发、高性能、分布式,性能优化,微服务 高级架构开发等等。
还有Java核心知识点+全套架构师学习资料和视频+一线大厂面试宝典+面试简历模板可以领取+阿里美团网易腾讯小米爱奇艺快手哔哩哔哩面试题+Spring源码合集+Java架构实战电子书+2021年最新大厂面试题。
ist.isEmpty();
}
最后
Java架构学习技术内容包含有:Spring,Dubbo,MyBatis, RPC, 源码分析,高并发、高性能、分布式,性能优化,微服务 高级架构开发等等。
还有Java核心知识点+全套架构师学习资料和视频+一线大厂面试宝典+面试简历模板可以领取+阿里美团网易腾讯小米爱奇艺快手哔哩哔哩面试题+Spring源码合集+Java架构实战电子书+2021年最新大厂面试题。
[外链图片转存中…(img-xuD6WLHt-1714443896323)]