?? Java学习:Java从入门到精通总结
?? 深入浅出RocketMQ设计思想:深入浅出RocketMQ设计思想
?? 绝对不一样的职场干货:大厂最佳实践经验指南
?? 最近更新:2022年5月24日
?? 个人简介:通信工程本硕??、Java程序员??。做过科研,发过专利,优秀的程序员不应该只是CRUD
?? 点赞 ?? 收藏 留言 ?? 都是我最大的动力!
文章目录
消息投递模型
在前几篇文章里我曾经也画过消息投递的模型图,这里再来简单复习一下:
-
消息生产者集群从注册中心获取到路由信息(负载均衡),然后将消息发送给
Broker
集群 -
注册中心是无状态集群,即每一台服务器都不影响其他的服务器。
Broker
会同时向所有的注册中心服务器里发送注册信息 -
注册中心存储的是
Topic
、Queue
、IP地址等信息,正常情况下每台机器存储的应该是相同的 -
Broker采用主从架构提供服务,主服务器负责写入操作,从服务器负责处理读请求
消息投递流程
消息发送的时序图如下图所示:
Producer
首先要知道向哪个Broker
发送消息,所以具体流程如下:
Producer
先从本地尝试获取路由信息- 本地无缓存的路由信息时,从注册中心中获取路由信息,并缓存到本地
- 获取到的路由信息包含了
Topic
下的所有Queue
,Producer
就可以采取负载均衡策略把消息发送到某个队列里 Producer
发送消息到Broker
成功之后,服务器就会返回消息发送成功对象SendResult
消息投递方法链
下面以时序图的形式展示了从获取路由表到消息投递过程的整体方法调用链:
上图涉及到的核心API如下:
// 发送消息
DefaultMQProducer#send(Message msg);
// 发送消息,增加超时时间
DefaultMQProducer#send(Message msg, long timeout);
// 发送消息,增加发送消息的模式(异步/同步)
DefaultMQProducer#sendDefaultImpl(Message msg, CommunicationMode mode, long timeout);
// 查询消息发送的路由信息
DefaultMQProducerImpl#tryToFindTopicPublishInfo(String topic);
// 根据topic的名称更新注册中心的路由信息
MQClientInstance#updateTopicRouteInfoFromNameServer(String topic);
// 根据topic的名称更新注册中心的路由信息,并获取路由信息
MQClientInstance#updateTopicRouteInfoFromNameServer(String topic, Boolean isDefault, MQDefaultProducer mqDefaultProducer);
// 根据负载均衡算法,选择一个队列进行消息发送
DefaultMQProducerImpl#selectOneMessageQueue(TopicPublishInfo topic, String lastBrokerName);
// 发送消息
DefaultMQProducerImpl#sendKernelImpl(Message msg, MessageQueue queue);
接下来我们进行源码级分析,可以对照上图学习:
SendResult
如果消息发送成功,会返回一个SendResult
对象:
/**
* 发送消息结果
*/
public class SendResult {
/**
* 发送消息结果状态
*/
private SendStatus sendStatus;
/**
* 消息的唯一key,由Client发送消息时生成
*/
private String msgId;
/**
* 消息队列
*/
private MessageQueue messageQueue;
/**
* 消息队列偏移量
*/
private long queueOffset;
/**
* 事务ID
*/
private String transactionId;
/**
* 下一条消息的偏移量
*/
private String offsetMsgId;
/**
* 区域ID
*/
private String regionId;
}
其中SendStatus
是一个枚举值:
public enum SendStatus {
SEND_OK,
FLUSH_DISK_TIMEOUT,
FLUSH_SLAVE_TIMEOUT,
SLAVE_NOT_AVAILABLE,
}
- SEND_OK:消息发送成功且存储同步成功
- FLUSH_DISK_TIMEOUT:消息发送成功但存储失败
- FLUSH_SLAVE_TIMEOUT:消息发送成功但slave节点超时
- SLAVE_NOT_AVAILABLE:消息发送成功但slave节点不可用