RocketMQ重试机制

消息重试分为两种:Producer发送消息的重试Consumer消息消费的重试

一、Producer端重试

Producer端重试是指: Producer往MQ上发消息没有发送成功,比如网络原因导致生产者发送消息到MQ失败。

看一下代码:

@Slf4j
public class RocketMQTest {
/**
* 生产者组
*/
private static String PRODUCE_RGROUP = "test_producer"; public static void main(String[] args) throws Exception {
//1、创建生产者对象
DefaultMQProducer producer = new DefaultMQProducer(PRODUCE_RGROUP);
//设置重试次数(默认2次)
producer.setRetryTimesWhenSendFailed(3000);
//绑定name server
producer.setNamesrvAddr("74.49.203.55:9876");
producer.start();
//创建消息
Message message = new Message("topic_family", ("小小今年3岁" ).getBytes());
//发送 这里填写超时时间是5毫秒 所以每次都会发送失败
SendResult sendResult = producer.send(message,5);
log.info("输出生产者信息={}",sendResult);
}
}

超时重试 针对网上说的超时异常会重试的说法都是错误的,想想都觉得可怕,我查的所以文章都说超时异常都会重试,难道这么多人都没有去测试一下 或者去看个源码。

我发现这个问题,是因为我上面超时时间设置为5毫秒 ,按照正常肯定会报超时异常,但我设置1次重试和3000次的重试,虽然最终都会报下面异常,但输出错误时间报

显然不应该是一个级别。但测试发现无论我设置的多少次的重试次数,报异常的时间都差不多。

org.apache.rocketmq.remoting.exception.RemotingTooMuchRequestException: sendDefaultImpl call timeout

针对这个疑惑,我去看了源码之后,才恍然大悟。

   /**
* 说明 抽取部分代码
*/
private SendResult sendDefaultImpl(Message msg, final CommunicationMode communicationMode, final SendCallback sendCallback, final long timeout) { //1、获取当前时间
long beginTimestampFirst = System.currentTimeMillis();
long beginTimestampPrev ;
//2、去服务器看下有没有主题消息
TopicPublishInfo topicPublishInfo = this.tryToFindTopicPublishInfo(msg.getTopic());
if (topicPublishInfo != null && topicPublishInfo.ok()) {
boolean callTimeout = false;
//3、通过这里可以很明显看出 如果不是同步发送消息 那么消息重试只有1次
int timesTotal = communicationMode == CommunicationMode.SYNC ? 1 + this.defaultMQProducer.getRetryTimesWhenSendFailed() : 1;
//4、根据设置的重试次数,循环再去获取服务器主题消息
for (times = 0; times < timesTotal; times++) {
MessageQueue mqSelected = this.selectOneMessageQueue(topicPublishInfo, lastBrokerName);
beginTimestampPrev = System.currentTimeMillis();
long costTime = beginTimestampPrev - beginTimestampFirst;
//5、前后时间对比 如果前后时间差 大于 设置的等待时间 那么直接跳出for循环了 这就说明连接超时是不进行多次连接重试的
if (timeout < costTime) {
callTimeout = true;
break; }
//6、如果超时直接报错
if (callTimeout) {
throw new RemotingTooMuchRequestException("sendDefaultImpl call timeout");
}
}
}

通过这段源码很明显可以看出以下几点

  1. 如果是异步发送 那么重试次数只有1次
  2. 对于同步而言,超时异常也是不会再去重试
  3. 如果发生重试是在一个for 循环里去重试,所以它是立即重试而不是隔一段时间去重试。

真是实践出真知!!!

二、 Consumer端重试

消费端比较有意思,而且在实际开发过程中,我们也更应该考虑的是消费端的重试。

消费者端的失败主要分为2种情况,ExceptionTimeout

1、Exception

@Slf4j
@Component
public class Consumer {
/**
* 消费者实体对象
*/
private DefaultMQPushConsumer consumer;
/**
* 消费者组
*/
public static final String CONSUMER_GROUP = "test_consumer";
/**
* 通过构造函数 实例化对象
*/
public Consumer() throws MQClientException {
consumer = new DefaultMQPushConsumer(CONSUMER_GROUP);
consumer.setNamesrvAddr("47.99.203.55:9876;47.99.203.55:9877");
//订阅topic和 tags( * 代表所有标签)下信息
consumer.subscribe("topic_family", "*");
//注册消费的监听 并在此监听中消费信息,并返回消费的状态信息
consumer.registerMessageListener((MessageListenerConcurrently) (msgs, context) -> {
//1、获取消息
Message msg = msgs.get(0);
try {
//2、消费者获取消息
String body = new String(msg.getBody(), "utf-8");
//3、获取重试次数
int count = ((MessageExt) msg).getReconsumeTimes();
log.info("当前消费重试次数为 = {}", count);
//4、这里设置重试大于3次 那么通过保存数据库 人工来兜底
if (count >= 2) {
log.info("该消息已经重试3次,保存数据库。topic={},keys={},msg={}", msg.getTopic(), msg.getKeys(), body);
return ConsumeConcurrentlyStatus.CONSUME_SUCCESS;
}
//直接抛出异常
throw new Exception("=======这里出错了============");
//return ConsumeConcurrentlyStatus.CONSUME_SUCCESS;
} catch (Exception e) {
e.printStackTrace();
return ConsumeConcurrentlyStatus.RECONSUME_LATER;
}
});
//启动监听
consumer.start();
}
}

这里的代码意思很明显: 主动抛出一个异常,然后如果超过3次,那么就不继续重试下去,而是将该条记录保存到数据库由人工来兜底。

看下运行结果

注意 消费者和生产者的重试还是有区别的,主要有两点

1、默认重试次数:Product默认是2次,而Consumer默认是16次

2、重试时间间隔:Product是立刻重试,而Consumer是有一定时间间隔的。它照1S,5S,10S,30S,1M,2M····2H进行重试。

3、Product在异步情况重试失效,而对于Consumer在广播情况下重试失效。

2、Timeout

说明 这里的超时异常并非真正意义上的超时,它指的是指获取消息后,因为某种原因没有给RocketMQ返回消费的状态,即没有return ConsumeConcurrentlyStatus.CONSUME_SUCCESSreturn ConsumeConcurrentlyStatus.RECONSUME_LATER

那么 RocketMQ会认为该消息没有发送,会一直发送。因为它会认为该消息根本就没有发送给消费者,所以肯定没消费。

做这个测试很简单。

        //1、消费者获得消息
String body = new String(msg.getBody(), "utf-8");
//2、获取重试次数
int count = ((MessageExt) msg).getReconsumeTimes();
log.info("当前消费重试次数为 = {}", count);
//3、这里睡眠60秒
Thread.sleep(60000);
log.info("休眠60秒 看还能不能走到这里。topic={},keys={},msg={}", msg.getTopic(), msg.getKeys(), body);
//返回成功
return ConsumeConcurrentlyStatus.CONSUME_SUCCESS;

当获得 当前消费重试次数为 = 0 后 , 关掉该进程。再重新启动该进程,那么依然能够获取该条消息

consumer消费者  当前消费重试次数为 = 0
休眠60秒 看还能不能走到这里。topic=topic_family,keys=1a2b3c4d5f,msg=小小今年3岁
只要自己变优秀了,其他的事情才会跟着好起来(上将2)

RocketMQ(5)---RocketMQ重试机制的更多相关文章

  1. RocketMQ重试机制和消息幂等

    一.重试机制 由于MQ经常处于复杂的分布式系统中,考虑网络波动,服务宕机,程序异常因素,很有可能出现消息发送或者消费失败的问题.因此,消息的重试就是所有MQ中间件必须考虑到的一个关键点.如果没有消息重 ...

  2. RocketMQ之八:重试队列,死信队列,消息轨迹

    问题思考 死信队列的应用场景? 死信队列中的数据是如何产生的? 如何查看死信队列中的数据? 死信队列的读写权限? 死信队列如何消费? 重试队列和死信队列的配置 消息轨迹 1.应用场景 一般应用在当正常 ...

  3. RocketMQ-消费重试机制

    介绍: RocketMQ的消息重试及时分为两种,一种是Producer端重试,一种是Consume端重试. 1.Producer端重试 : 1.1消息发没发成功,默认情况下是3次重试. 2.Consu ...

  4. Rocket重试机制,消息模式,刷盘方式

    一.Consumer 批量消费(推模式) 可以通过 consumer.setConsumeMessageBatchMaxSize(10);//每次拉取10条 这里需要分为2种情况 Consumer端先 ...

  5. ENode 1.0 - 消息的重试机制的设计思路

    项目开源地址:https://github.com/tangxuehua/enode 上一篇文章,简单介绍了enode框架中消息队列的设计思路,本文介绍一下enode框架中关系消息的重试机制的设计思路 ...

  6. 【Dubbo 源码解析】07_Dubbo 重试机制

    Dubbo 重试机制 通过前面 Dubbo 服务发现&引用 的分析,我们知道,Dubbo 的重试机制是通过 com.alibaba.dubbo.rpc.cluster.support.Fail ...

  7. Spring Cloud 请求重试机制核心代码分析

    场景 发布微服务的操作一般都是打完新代码的包,kill掉在跑的应用,替换新的包,启动. spring cloud 中使用eureka为注册中心,它是允许服务列表数据的延迟性的,就是说即使应用已经不在服 ...

  8. Volley超时重试机制

    基础用法 Volley为开发者提供了可配置的超时重试机制,我们在使用时只需要为我们的Request设置自定义的RetryPolicy即可. 参考设置代码如下: int DEFAULT_TIMEOUT_ ...

  9. SpringCloud | FeignClient和Ribbon重试机制区别与联系

    在spring cloud体系项目中,引入的重试机制保证了高可用的同时,也会带来一些其它的问题,如幂等操作或一些没必要的重试. 今天就来分别分析一下 FeignClient 和 Ribbon 重试机制 ...

随机推荐

  1. WPF的逻辑树与视觉树(3)Visual呈现

    原文:WPF的逻辑树与视觉树(3)Visual呈现 这篇就点到为止,挑重点讲 绘图方式有两种 1.继承UIElement,重写OnRender方法 public partial class Windo ...

  2. Cocos2dx 温馨提示(十)三种缓存类演示

    在介绍了三级缓存,首先,我们必须弄清楚的一个问题:什么是纹理?它的通俗的解释,质感是它的含义的图片. 当图像加载到内存中后,,它是存在于纹理的形式.由于从这里可以看出.纹理事实上就是一块内存.这块内存 ...

  3. 两个QWidget叠加,可部分代替layout的功能

    在QT开发过程中,有时候会遇到这样的问题,当我们自己创建了一个Layout对象以后,使用QWidget的setLayout方法,将这个Layout对象应用到窗口中的时候,发现窗口上没有我们添加的控件, ...

  4. WPF中制作立体效果的文字或LOGO图形

    原文:WPF中制作立体效果的文字或LOGO图形 较久之前,我曾写过一篇:"WPF绘制党徽(立体效果,Cool) "的博文.有感兴趣的朋友来EMAIL问是怎么制作的?本文解决此类问题 ...

  5. Linux(CentOS 7)+ Nginx(1.10.2)+ Mysql(5.7.16)+ PHP(7.0.12)完整环境搭建

    首先安装Linux系统,我以虚拟机安装来做示例,先去下载 VitualBox,这是一款开源的虚拟机软件,https://www.virtualbox.org 官网地址.或者是VMware,www.vm ...

  6. 基于VC面部识别软件(识别出人脸特征)

    最近在学校MFC和Opencv,毕业只是做面部识别软件,所以写一个,我觉得很简单. 第一个VC组态opencv.不用说.非常easy,网上资料非常多,然后就是设计界面啦,这个全然凭个人喜好了,我设计的 ...

  7. thinkphp 删除所有缓存 Rumtime 以及 Html 静态缓存

    <?php /** * This is not a free software, All Copyright @F.Z.B * Date: 14-8-12 下午4:08 * File: Cach ...

  8. 用树莓派和DS18B20做个汽车温度记录仪

    原文:用树莓派和DS18B20做个汽车温度记录仪 用树莓派和DS18B20做个汽车温度记录仪[原创] 很想知道夏日阳光暴晒下,汽车内的最高温度以及温度的变化情况.觉得用树莓派和DS18B20来实现应该 ...

  9. #467 – 使用UniformGrid 均分行和列(Use a UniformGrid for Evenly Spaced Rows and Columns)

    原文 #467 – 使用UniformGrid 均分行和列(Use a UniformGrid for Evenly Spaced Rows and Columns) UniformGrid 布局面板 ...

  10. WPF中的多进程(Threading)处理实例(一)

    原文:WPF中的多进程(Threading)处理实例(一) 说明:希望通过揣摩这些案例,能进一步了解进程的工作原理. 1.方法一描述的是在同一窗口中,在计算素数的同时而不影响Canvas的工作. 方法 ...