1. 前言
本文主要是介绍一下RocketMQ
消息生产者在发送消息的时候发送失败的问题处理?这里有两个点,一个是关于消息的处理,一个是关于broker的处理,比如说发送消息到broker-a的broker失败了,我们可能下次就不想发送到这个broker-a,这就涉及到一个选择broker的问题,也就是选择MessageQueue的问题。
2. 失败重试
其实失败重试我们在介绍RocketMQ
消息生产者发送消息的时候介绍过了,其实同步发送与异步发送都会失败重试
的,比如说我发送一个消息,然后超时了,这时候在MQProducer
层就会进行控制重试,默认是重试2次
的,加上你发送那次,一共是发送3次
,如果重试完还是有问题的话,这个时候就会抛出异常了。
我们来看下这一块的代码实现( DefaultMQProducerImpl
类sendDefaultImpl
方法):
这块其实就是用for循环实现的,其实不光RocketMQ
,分布式远程调用框架Dubbo
的失败重试也是用for循环实现的。
3. 延迟故障
我们都知道,在RocketMQ
中一个topic
其实是有多个MessageQueue
这么一个概念的,然后这些MessageQueue
可能对应着不同的broker name
,比如说id是0和1的MessageQueue
对应的broker name是 broker-a
,然后id是2和3的MessageQueue
对应的broker name
是broker-b
我们发送消息的时候,其实涉及到发送给哪个MessageQueue
这么一个问题,当然我们可以在发送消息的时候指定这个MessageQueue
,如果你不指定的话,RocketMQ
就会根据MQFaultStrategy
这么一个策略类给选择出来一个MessageQueue
。
我们先来看下是在哪里选择的,其实就是在我们重试的循环中: org.apache.rocketmq.client.impl.producer.DefaultMQProducerImpl#sendDefaultImpl
...
// 重试发送
for (; times < timesTotal; times++) {
String lastBrokerName = null == mq ? null : mq.getBrokerName();
// todo 选择message queue
MessageQueue mqSelected = this.selectOneMessageQueue(topicPublishInfo, lastBrokerName);
...
复制代码
我们可以看到,它会把topicPublishInfo
与 lastBrokerName
作为参数传进去,topicPublishInfo
里面其实就是那一堆MessageQueue
, 然后这个lastBrokerName
是上次我们选择的那个broker name
, 这个接着我们来看下这个selectOneMessageQueue
实现:
public MessageQueue selectOneMessageQueue(final TopicPublishInfo tpInfo, final String lastBrokerName) {
// todo
return this.mqFaultStrategy.selectOneMessageQueue(tpInfo, lastBrokerName);
}
复制代码
可以看到它调用了MQFaultStrategy
这个类的selectOneMessageQueue
方法,我们接着进去:
public MessageQueue selectOneMessageQueue(final TopicPublishInfo tpInfo, final String lastBrokerName) {
// 发送延迟故障启用,默认为false
if (this.sendLatencyFaultEnable) {
try {
// 获取一个index
int index = tpInfo.getSendWhichQueue().getAndIncrement();
for (int i = 0; i < tpInfo.getMessageQueueList().size(); i++) {
int pos = Math.abs(index++) % tpInfo.getMessageQueueList().size();
if (pos < 0)
pos = 0;
MessageQueue mq = tpInfo.getMessageQueueList().get(pos);
// 选取的这个broker是可用的 直接返回
if (latencyFaultTolerance.isAvailable(mq.getBrokerName()))