引言
rocketmq支持3种发送消息方式:同步,异步,单向(oneway)
主要关注的点:
- rocketmq消息结构
- 生产者的启动流程
- 消息发送过程
- 批量消息发送过程
主要的问题是:
消息队列如何负载?
生产者发送消息时,如果本地缓存表没有路由信息,会从NameServer更新路由信息。每隔30s,会从NameServer更新路由信息表
消息发送如何实现高可用?
- 会在超时时间范围内进行重试
- 会在发送时,通过故障策略提供发送消息的成功率
批量消息发送如何实现一致性?
把批量消息封装成MessageBatch,一次性发送。
注意点
- 一个productGroup 在一个jvm上只要有一个product实例
- MQClientManager是单例
- MQClientInstance是客户端与name server和broker直接沟通对象。 一个jvm内的消费者和生产者公用一个MQClientInstance。
- 想要在运行时停mq的流程,只需调用MQProduct的shutdown方法,会移除本地的生产者列表,并在broker注销。重启只需要调用下面的封装方法。 使用JMX管理,就可以实现运行时停止和开启流量了。
public void restart(DefaultMQProducer producer){
if (producer.getDefaultMQProducerImpl().getServiceState()
.equals(ServiceState.SHUTDOWN_ALREADY)) {
producer.getDefaultMQProducerImpl().setServiceState(ServiceState.CREATE_JUST);
producer.start();
}
}
Message
用户发送消息封装类是Message
和MessageBatch
,分别是普通消息和批量消息
Message源码解析
MQProducer接口
用户接口,定义了发送消息的方法
有两个实现类,DefaultMQProducer和TransactionProducer
TransactionProducer是继承DefaultMQProducer,实现了事务消息的发送
public interface MQProducer extends MQAdmin {
void start() ;
void shutdown();
List<MessageQueue> fetchPublishMessageQueues(final String topic) ;
// 同步方法 包含重试
SendResult send(final Message msg) ;
// 同步方法 指定本次总体发送超时时间,超过时间会直接返回
SendResult send(final Message msg, final long timeout) ;
// 异步方式 包含重试
void send(final Message msg, final SendCallback sendCallback) ;
// 异步方式 指定本次总体发送超时时间,超过时间会直接返回
void send(final Message msg, final SendCallback sendCallback, final long timeout)
// 单向
void sendOneway(final Message msg) ;
// 同步方法 会发往指定broker的指定队列
SendResult send(final Message msg, final MessageQueue mq) ;
// 同步方法 会发往指定broker的指定队列 指定本次总体发送超时时间
SendResult send(final Message msg, final MessageQueue mq, final long timeout)
// 异步方法 会发往指定broker的指定队列
void send(final Message msg, final MessageQueue mq, final SendCallback sendCallback)
// 异步方法 会发往指定broker的指定队列 指定本次总体发送超时时间
void send(final Message msg, final MessageQueue mq, final SendCallback sendCallback, long timeout)
// 单向 会发往指定broker的指定队列
void sendOneway(final Message msg, final MessageQueue mq) ;
// 同步方法 由发送方实现selector选择哪个broker的哪个queue
SendResult send(final Message msg, final MessageQueueSelector selector, final Object arg)
// 同步方法 由发送方实现selector选择哪个broker的哪个queue 指定本次总体发送超时时间
SendResult send(final Message msg, final MessageQueueSelector selector, final Object arg,
final long timeout) ;
// 异步方法 由发送方实现selector选择哪个broker的哪个queue
void send(final Message msg, final MessageQueueSelector selector, final Object arg,
final SendCallback sendCallback) ;
// 异步方法 由发送方实现selector选择哪个broker的哪个queue 指定本次总体发送超时时间
void send(final Message msg, final MessageQueueSelector selector, final Object arg,
final SendCallback sendCallback, final long timeout) ;
// 单向 由发送方实现selector选择哪个broker的哪个queue
void sendOneway(final Message msg, final MessageQueueSelector selector, final Object arg)
// DefaultMQProducer不实现此方法
TransactionSendResult sendMessageInTransaction(final Message msg,
final LocalTransactionExecuter tranExecuter, final Object arg) ;
// DefaultMQProducer不实现此方法
TransactionSendResult sendMessageInTransaction(final Message msg,
final Object arg) ;
//for batch
// 同步方式 比单个消息多一个消息打包过程
SendResult send(final Collection<Message> msgs) ;
// 同步方式 比单个消息多一个消息打包过程 指定本次总体发送超时时间
SendResult send(final Collection<Message> msgs, final long timeout) ;
// 同步方式 比单个消息多一个消息打包过程 会发往指定broker的指定队列
SendResult send(final Collection<Message> msgs, final MessageQueue mq) ;
// 同步方式 比单个消息多一个消息打包过程 会发往指定broker的指定队列 指定本次总体发送超时时间
SendResult send(final Collection<Message> msgs, final MessageQueue mq, final long timeout)
//for rpc
//同步方法 等待消费者发送ack才返回 指定本次总体发送超时时间
Message request(final Message msg, final long timeout) ;
//异步方法 等待消费者发送ack后执行callback 指定本次总体发送超时时间
void request(final Message msg, final RequestCallback requestCallback, final long timeout)
//同步方法 等待消费者发送ack才返回 由发送方实现selector选择哪个broker的哪个queue 指定本次总体发送超时时间
Message request(final Message msg, final MessageQueueSelector selector, final Object arg,
final long timeout) ;
//异步方法 等待消费者发送ack后执行callback 由发送方实现selector选择哪个broker的哪个queue 指定本次总体发送超时时间
void request(final Message msg, final MessageQueueSelector selector, final Object arg,
final RequestCallback requestCallback,
final long timeout) ;
//同步方法 等待消费者发送ack才返回 会发往指定broker的指定队列 指定本次总体发送超时时间
Message request(final Message msg, final MessageQueue mq, final long timeout)
//异步方法 等待消费者发送ack后执行callback 会发往指定broker的指定队列 指定本次总体发送超时时间
void request(final Message msg, final MessageQueue mq, final RequestCallback requestCallback, long timeout)
}
生产者消息发送方式
- 消息的发送方式有三种:同步,异步,单向
- 消息发送的条数有两种:单条消息和批量消息
- 发送完成的时机:broker接收和consumer ack
- 消息的类型有两种:普通消息和事务消息,事务消息需要使用TransactionMQProducer
DefaultMQProducer
- 实现了MQProducer接口,是面向发送普通消息的用户
- DefaultMQProducer是默认的消息生产者实现类 UML图如下
- MQAdmin接口主要是管理员的角色:创建topic和根据offset查询消息
- MQProducer接口定义了发送消息的几种方式
- ClientConfig类是客户端的配置,对于rocketmq来说生产者和消费者都是客户端
- 持有DefaultMQProducerImpl来处理具体业务逻辑
类图
主要属性
protected final transient DefaultMQProducerImpl defaultMQProducerImpl;
//所有的生产者根据producerGroup认为是一个集群。主要用于发送事务消息时,消息状态的回查,调用producerGroup中的任意一个
private String producerGroup;
// 提供了一个测试和demo的topic
private String createTopicKey = MixAll.AUTO_CREATE_TOPIC_KEY_TOPIC;
// 创建默认topic的队列数量
private volatile int defaultTopicQueueNums = 4;
//发送消息的超时时间,3s
private int sendMsgTimeout = 3000;
//消息压缩的阀值,超过4kb就会压缩
private int compressMsgBodyOverHowmuch = 1024 * 4;
//producer发送消息给broker时会尝试多次。默认重复2次,一共三次
private int retryTimesWhenSendFailed = 2;
//发送异步消息的失败次数。
private int retryTimesWhenSendAsyncFailed = 2;
//broker消息存储失败,是否发送消息给其他broker 默认false
private boolean retryAnotherBrokerWhenNotStoreOK = false;
//最大的消息体,支持最大的值为2^32 -8
private int maxMessageSize = 1024 * 1024 * 4; // 4M
//不知道是个啥 fixme
private TraceDispatcher traceDispatcher = null;
启动流程
主要是启动defaultMQProducerImpl
defaultMQProducerImpl
public void start() throws MQClientException {
this.setProducerGroup(withNamespace(this.producerGroup));
this.defaultMQProducerImpl.start();
if (null != traceDispatcher) {
try {
traceDispatcher.start(this.getNamesrvAddr(), this.getAccessChannel());
} catch (MQClientException e) {
log.warn("trace dispatcher start failed ", e);
}
}
}
同步消息发送流程图
普通消息
自动选择消息队列的三种消息发送方式
DefaultMQProducer
// 同步发送
public SendResult send(
Message msg) throws MQClientException, RemotingException, MQBrokerException, InterruptedException {
// 校验消息
// msg不能空 body不能为0 且要符合生产者配置的最大消息大小。 默认是4m
// topic 不能空 不能大于127个字符 符合正则"^[%|a-zA-Z0-9_-]+$" 不能是“TBW102”
Validators.checkMessage(msg, this);
// 业务topic withNamespace方法无用
msg.setTopic(withNamespace(msg.getTopic()));
return this.defaultMQProducerImpl.send(msg);
}
// 异步
public void send(Message msg, SendCallback sendCallback, long timeout)
throws MQClientException, RemotingException, InterruptedException {
msg.setTopic(withNamespace(msg.getTopic()));
this.defaultMQProducerImpl.send(msg, sendCallback, timeout);
}
// oneway
public void sendOneway(Message msg) throws MQClientException, RemotingException, InterruptedException {
msg.setTopic(withNamespace(msg.getTopic()));
this.defaultMQProducerImpl.sendOneway(msg);
}
// 同步异步和oneway都是调用的sendDefaultImpl
指定消息队列发送消息
由用户指定发送broker的哪个queueId
// sync
public SendResult send(Message msg, MessageQueue mq)
throws MQClientException, RemotingException, MQBrokerException, InterruptedException {
msg.setTopic(withNamespace(msg.getTopic()));
return this.defaultMQProducerImpl.send(msg, queueWithNamespace(mq));
}
// async
public void send(Message msg, MessageQueue mq, SendCallback sendCallback)
throws MQClientException, RemotingException, InterruptedException {
msg.setTopic(withNamespace(msg.getTopic()));
this.defaultMQProducerImpl.send(msg, queueWithNamespace(mq), sendCallback);
}
// oneway
public void sendOneway(Message msg,
MessageQueue mq) throws MQClientException, RemotingException, InterruptedException {
msg.setTopic(withNamespace(msg.getTopic()));
this.defaultMQProducerImpl.sendOneway(msg, queueWithNamespace(mq));
}
提供消息队列选择器发送消息
提供消息队列选择器发送消息
消息队列选择器
public interface MessageQueueSelector {
MessageQueue select(final List<MessageQueue> mqs, final Message msg, final Object arg);
}
// 同步
public SendResult send(Message msg, MessageQueueSelector selector, Object arg, long timeout)
throws MQClientException, RemotingException, MQBrokerException, InterruptedException {
msg.setTopic(withNamespace(msg.getTopic()));
return this.defaultMQProducerImpl.send(msg, selector, arg, timeout);
}
// 异步
public void send(Message msg, MessageQueueSelector selector, Object arg, SendCallback sendCallback, long timeout)
throws MQClientException, RemotingException, InterruptedException {
msg.setTopic(withNamespace(msg.getTopic()));
this.defaultMQProducerImpl.send(msg, selector, arg, sendCallback, timeout);
}
// oneway
public void sendOneway(Message msg, MessageQueueSelector selector, Object arg)
throws MQClientException, RemotingException, InterruptedException {
msg.setTopic(withNamespace(msg.getTopic()));
this.defaultMQProducerImpl.sendOneway(msg, selector, arg);
}
批量消息
发送批量消息只支持同步模式
提供自动选择队列和提供消息队列两种队列选择模式
// 自动选择队列
public SendResult send(Collection<Message> msgs,
long timeout) throws MQClientException, RemotingException, MQBrokerException, InterruptedException {
return this.defaultMQProducerImpl.send(batch(msgs), timeout);
}
// 提供队列模式
public SendResult send(Collection<Message> msgs, MessageQueue messageQueue,
long timeout) throws MQClientException, RemotingException, MQBrokerException, InterruptedException {
return this.defaultMQProducerImpl.send(batch(msgs), messageQueue, timeout);
}
封装批量消息
MessageBatch源码解析
private MessageBatch batch(Collection<Message> msgs) throws MQClientException {
MessageBatch msgBatch;
try {
msgBatch = MessageBatch.generateFromList(msgs);
for (Message message : msgBatch) {
Validators.checkMessage(message, this);
// fixme
MessageClientIDSetter.setUniqID(message);
message.setTopic(withNamespace(message.getTopic()));
}
msgBatch.setBody(msgBatch.encode());
} catch (Exception e) {
throw new MQClientException("Failed to initiate the MessageBatch", e);
}
msgBatch.setTopic(withNamespace(msgBatch.getTopic()));
return msgBatch;
}
RPC消息
先来讲讲为啥叫rpc消息呢?
- 可以去看MQProducer接口对于request方法发现消息的分类
rpc消息和普通消息的区别:
- 普通消息是等待broker接收消息(或者store ok)后返回结果
- (同步模式下)rpc消息是等待broker接收后,consumer消费后返回Message给到producer才算结束。
- 一个是发送完成就ok 一个是等待consumer消费完成并返回结果才ok
支持同步和异步,因为oneway对于rpc无意义
支持三种队列选择模式
自动选择队列
// 同步
public Message request(final Message msg, final long timeout) throws RequestTimeoutException, MQClientException,
RemotingException, MQBrokerException, InterruptedException {
msg.setTopic(withNamespace(msg.getTopic()));
return this.defaultMQProducerImpl.request(msg, timeout);
}
// 异步
public void request(final Message msg, final RequestCallback requestCallback, final long timeout)
throws MQClientException, RemotingException, InterruptedException, MQBrokerException {
msg.setTopic(withNamespace(msg.getTopic()));
this.defaultMQProducerImpl.request(msg, requestCallback, timeout);
}
队列选择器模式
public Message request(final Message msg, final MessageQueueSelector selector, final Object arg,
final long timeout) throws MQClientException, RemotingException, MQBrokerException,
InterruptedException, RequestTimeoutException {
msg.setTopic(withNamespace(msg.getTopic()));
return this.defaultMQProducerImpl.request(msg, selector, arg, timeout);
}
public void request(final Message msg, final MessageQueueSelector selector, final Object arg,
final RequestCallback requestCallback, final long timeout) throws MQClientException, RemotingException,
InterruptedException, MQBrokerException {
msg.setTopic(withNamespace(msg.getTopic()));
this.defaultMQProducerImpl.request(msg, selector, arg, requestCallback, timeout);
}
指定消息队列
public Message request(final Message msg, final MessageQueue mq, final long timeout)
throws MQClientException, RemotingException, MQBrokerException, InterruptedException, RequestTimeoutException {
msg.setTopic(withNamespace(msg.getTopic()));
return this.defaultMQProducerImpl.request(msg, mq, timeout);
}
public void request(final Message msg, final MessageQueue mq, final RequestCallback requestCallback, long timeout)
throws MQClientException, RemotingException, InterruptedException, MQBrokerException {
msg.setTopic(withNamespace(msg.getTopic()));
this.defaultMQProducerImpl.request(msg, mq, requestCallback, timeout);
}