JAVA后端开发知识总结(持续更新…)
Producer:将需要保证顺序的消息按某个属性来自定义选择 MessageQueue,相同属性的分到同一个MessageQueue,然后采取同步发送的方式发送,这时也能保证前一个消息发送成功并返回响应后,才能发送下一个。
broker:因为Producer采用同步发送的方式,broker 会先接收到消息1,然后将消息1追加写到commitLog中,然后记录下这个消息在queue里面的offset,这个offset是有顺序的,然后刷盘操作,最后返回消息1的响应,然后Producer才能发送消息2。
public SendResult send(Message msg, MessageQueueSelector selector, Object arg) throws MQClientException, RemotingException,
MQBrokerException, InterruptedException {
return metaProducerImpl.send(EnvPlugin.wrapperMsg(msg), selector, arg);
}
...
// 一路调用到 DefaultMQProducerImpl 的 send()
public SendResult send(Message msg, MessageQueueSelector selector, Object arg, long timeout)
throws MQClientException, RemotingException, MQBrokerException, InterruptedException {
// SYNC 表明 producer 是同步发送消息的,会等待 broker 的响应
return this.sendSelectImpl(msg, selector, arg, CommunicationMode.SYNC, null, timeout);
}
send()的最后一个参数,会作为参数被传给select方法中的arg。
private SendResult sendSelectImpl(Message msg, MessageQueueSelector selector, Object arg,
final CommunicationMode communicationMode,
final SendCallback sendCallback, final long timeout
) throws MQClientException, RemotingException, MQBrokerException, InterruptedException {
this.makeSureStateOK();
Validators.checkMessage(msg, this.defaultMQProducer);
// 获取 Topic 信息
TopicPublishInfo topicPublishInfo = this.tryToFindTopicPublishInfo(msg.getTopic());
// messageQueueList 不为空
if (topicPublishInfo != null && topicPublishInfo.ok()) {
MessageQueue mq = null;
try {
// 获取当前 topic 的内部队列
List<MessageQueue> messageQueueList =
mQClientFactory.getMQAdminImpl().parsePublishMessageQueues(topicPublishInfo.getMessageQueueList());
// 克隆消息
Message userMessage = MessageAccessor.cloneMessage(msg);
String userTopic = NamespaceUtil.withoutNamespace(userMessage.getTopic(), mQClientFactory.getClientConfig().getNamespace());
userMessage.setTopic(userTopic);
// 调用自定义的 select() 方法,传入 send() 的第三个参数(例如:orderId)作为选择队列的关键字
mq = mQClientFactory.getClientConfig().queueWithNamespace(selector.select(messageQueueList, userMessage, arg));
} catch (Throwable e) {
throw new MQClientException("select message queue throwed exception.", e);
}
// 发送请求的逻辑部分,与发送普通消息是一样的
if (mq != null) {
return this.sendKernelImpl(msg, mq, communicationMode, sendCallback, null, timeout);
} else {
throw new MQClientException("select message queue return null.", null);
}
}
throw new MQClientException("No route info for this topic, " + msg.getTopic(), null);
}
Consumer:一个MessageQueue对应生成一个processQueue,然后向broker从特定offset拿一批消息过来,取过来时消息也是顺序排列的。然后Consumer会将这批消息放到 processQueue中的TreeMap(有序结构)中,调用自定义实现的MessageListenerOrderly的类对象进行单线程消费。
顺序消费消息情况:
《RocketMQ学习(四)——RocketMQ消息发送》
《RocketMQ学习(八)——RocketMQ顺序消息》
《RocketMQ源码目录篇》