RocketMQ(11) 消息重试机制和死信队列
七、消息发送重试机制
1 说明
Producer对发送失败的消息进行重新发送的机制,称为消息发送重试机制,也称为消息重投机制。
对于消息重投,需要注意以下几点:
生产者在发送消息时,若采用同步或异步发送方式,发送失败会重试,但oneway消息发送方式 发送失败是没有重试机制的
只有普通消息有发送重试机制,顺序消息是没有的(只有默认自带的发送选择才有这个功能,若手动实现选择器,则无法实现重试避错机制,也不需要)
消息重投机制可以保证消息尽可能发送成功、不丢失,但可能会造成消息重复。消息重复在 RocketMQ中是无法避免的问题
消息重复在一般情况下不会发生,当出现消息量大、网络抖动,消息重复就会成为大概率事件
producer主动重发、consumer负载变化(发生Rebalance,不会导致消息重复,但可能出现重复 消费)也会导致重复消息
消息重复无法避免,但要避免消息的重复消费。
避免消息重复消费的解决方案是,为消息添加唯一标识(例如消息key),使消费者对消息进行消 费判断来避免重复消费
消息发送重试有三种策略可以选择:同步发送失败策略、异步发送失败策略、消息刷盘失败策略
2 同步发送失败策略
对于普通消息,消息发送默认采用round-robin策略来选择所发送到的队列。如果发送失败,默认重试2 次。但在重试时是不会选择上次发送失败的Broker,而是选择其它Broker。当然,若只有一个Broker其 也只能发送到该Broker,但其会尽量发送到该Broker上的其它Queue。
// 创建一个producer,参数为Producer Group名称
DefaultMQProducer producer = new DefaultMQProducer("pg");
// 指定nameServer地址
producer.setNamesrvAddr("rocketmqOS:9876");
// 设置同步发送失败时重试发送的次数,默认为2次
producer.setRetryTimesWhenSendFailed(3);
// 设置发送超时时限为5s,默认3s
producer.setSendMsgTimeout(5000);
同时,Broker还具有失败隔离功能,使Producer尽量选择未发生过发送失败的Broker作为目标 Broker。其可以保证其它消息尽量不发送到问题Broker,可以降低发送失败的概率,提升消息发送效率,降低消息发送耗时。
思考:让我们自己实现失败隔离功能,如何来做?
1)方案一:Producer中维护某JUC的Map集合,其key是发生失败的时间戳,value为Broker实 例。Producer中还维护着一个Set集合,其中存放着所有未发生发送异常的Broker实例。选择目 标Broker是从该Set集合中选择的。再定义一个定时任务,定期从Map集合中将长期未发生发送 异常的Broker清理出去,并添加到Set集合。
2)方案二:为Producer中的Broker实例添加一个标识,例如是一个AtomicBoolean属性。只要该 Broker上发生过发送异常,就将其置为true。选择目标Broker就是选择该属性值为false的 Broker。再定义一个定时任务,定期将Broker的该属性置为false。
3)方案三:为Producer中的Broker实例添加一个标识,例如是一个AtomicLong属性。只要该 Broker上发生过发送异常,就使其值增一。选择目标Broker就是选择该属性值最小的Broker。若 该值相同,采用轮询方式选择。
如果超过重试次数,则抛出异常,由Producer去保证消息不丢。当然当生产者出现 RemotingException、MQClientException和MQBrokerException时,Producer会自动重投消息。
3 异步发送失败策略
异步发送失败重试时,异步重试不会选择其他broker,仅在同一个broker上做重试,所以该策略无法保 证消息不丢。
DefaultMQProducer producer = new DefaultMQProducer("pg");
producer.setNamesrvAddr("rocketmqOS:9876");
producer.setRetryTimesWhenSendAsyncFailed(3);
4 消息刷盘失败策略
消息刷盘超时(Master或Slave)或slave不可用(slave在做数据同步时向master返回状态不是 SEND_OK)时,默认是不会将消息尝试发送到其他Broker的。不过,对于重要消息可以通过在Broker 的配置文件设置retryAnotherBrokerWhenNotStoreOK属性为true来开启。
八、消息消费重试机制
1 顺序消息的消费重试
对于顺序消息,需要严格保证拉取消费的顺序性,所以不会错过任何一个消息的消费,所以当Consumer消费消息失败后,为了保证消息的顺序性,其会自动不断地进行消息重 试,直到消费成功。消费重试默认间隔时间为1000毫秒。重试期间应用会出现消息消费被阻塞的情 况。
DefaultMQPushConsumer consumer = new DefaultMQPushConsumer("cg");
// 顺序消息消费失败的消费重试时间间隔,单位毫秒,默认为1000,其取值范围为[10,30000]
consumer.setSuspendCurrentQueueTimeMillis(100);
由于对顺序消息的重试是无休止的,不间断的,直至消费成功,所以,对于顺序消息的消费, 务必要保证应用能够及时监控并处理消费失败的情况,避免消费被永久性阻塞。
注意,顺序消息没有发送失败重试机制,但具有消费失败重试机制
2 无序消息的消费重试
对于无序消息(普通消息、延时消息、事务消息),当Consumer消费消息失败时,可以通过设置返回状态达到消息重试的效果。不过需要注意,无序消息的重试只对集群消费方式生效,广播消费方式不提供失败重试特性。即对于广播消费,消费失败后,失败消息不再重试,继续消费后续消息。
对于无序消息集群消费下的重试消费,每条消息默认最多重试16次,但每次重试的间隔时间是不同的,会逐渐变长。每次重试的间隔时间如下表。
| 重试次数 | 与上次重试的间隔时间 | 重试次数 | 与上次重试的间隔时间 |
|---|---|---|---|
| 1 | 10秒 | 9 | 7分钟 |
| 2 | 30秒 | 10 | 8分钟 |
| 3 | 1分钟 | 11 | 9分钟 |
| 4 | 2分钟 | 12 | 10分钟 |
| 5 | 3分钟 | 13 | 20分钟 |
| 6 | 4分钟 | 14 | 30分钟 |
| 7 | 5分钟 | 15 | 1小时 |
| 8 | 6分钟 | 16 | 2小时 |
若一条消息在一直消费失败的前提下,将会在正常消费后的第4小时46分后进行第16次重试。 若仍然失败,则将消息投递到死信队列
修改消费重试次数
DefaultMQPushConsumer consumer = new DefaultMQPushConsumer("cg");
// 修改消费重试次数
consumer.setMaxReconsumeTimes(10);对于修改过的重试次数,将按照以下策略执行:
- 若修改值小于16,则按照指定间隔进行重试
- 若修改值大于16,则超过16次的重试时间间隔均为2小时
对于Consumer Group,若仅修改了一个Consumer的消费重试次数,则会应用到该Group中所有 其它Consumer实例。若出现多个Consumer均做了修改的情况,则采用覆盖方式生效。即最后被 修改的值会覆盖前面设置的值。
3. 重试队列
对于需要重试消费的消息,并不是Consumer在等待了指定时长后再次去拉取原来的消息进行消费,而 是将这些需要重试消费的消息放入到了一个特殊Topic的队列中,而后进行再次消费的。这个特殊的队 列就是重试队列。
当出现需要进行重试消费的消息时,Broker会为每个消费组都设置一个Topic名称 为%RETRY%consumerGroup@consumerGroup 的重试队列。
1)这个重试队列是针对消息才组的,而不是针对每个Topic设置的(一个Topic的消息可以让多 个消费者组进行消费,所以会为这些消费者组各创建一个重试队列)
2)只有当出现需要进行重试消费的消息时,才会为该消费者组创建重试队列
注意,消费重试的时间间隔与延时消费的延时等级十分相似,除了没有延时等级的前两个时间 外,其它的时间都是相同的
Broker对于重试消息的处理是通过延时消息实现的。先将消息保存到SCHEDULE_TOPIC_XXXX延迟队 列中,延迟时间到后,会将消息投递到%RETRY%consumerGroup@consumerGroup重试队列中。
4. 消费重试配置方式
集群消费方式下,消息消费失败后若希望消费重试,则需要在消息监听器接口的实现中明确进行如下三 种方式之一的配置:
- 方式1:返回ConsumeConcurrentlyStatus.RECONSUME_LATER(推荐)
- 方式2:返回Null
- 方式3:抛出异常

5. 消费不重试配置方式

集群消费方式下,消息消费失败后若不希望消费重试,则在捕获到异常后同样也返回与消费成功后的相 同的结果,即ConsumeConcurrentlyStatus.CONSUME_SUCCESS,则不进行消费重试。
九、死信队列
1 什么是死信队列
当一条消息初次消费失败,消息队列会自动进行消费重试;达到最大重试次数后,若消费依然失败,则 表明消费者在正常情况下无法正确地消费该消息,此时,消息队列不会立刻将消息丢弃,而是将其发送 到该消费者对应的特殊队列中。这个队列就是死信队列(Dead-Letter Queue,DLQ),而其中的消息 则称为死信消息(Dead-Letter Message,DLM)。
死信队列是用于处理无法被正常消费的消息的。
2 死信队列的特征
死信队列具有如下特征:
死信队列中的消息不会再被消费者正常消费,即DLQ对于消费者是不可见的
死信存储有效期与正常消息相同,均为 3 天(commitlog文件的过期时间),3 天后会被自动删除
死信队列就是一个特殊的Topic,名称为
%DLQ%consumerGroup@consumerGroup,即每个消 费者组都有一个死信队列如果⼀个消费者组未产生死信消息,则不会为其创建相应的死信队列
3 死信消息的处理
实际上,当⼀条消息进入死信队列,就意味着系统中某些地方出现了问题,从而导致消费者无法正常消 费该消息,比如代码中原本就存在Bug。因此,对于死信消息,通常需要开发人员进行特殊处理。最关 键的步骤是要排查可疑因素,解决代码中可能存在的Bug,然后再将原来的死信消息再次进行投递消 费
RocketMQ(11) 消息重试机制和死信队列的更多相关文章
- RabbitMQ与.net core(四) 消息的优先级 与 死信队列
1.消息的优先级 假如现在有个需求,我们需要让一些优先级最高的通知推送到客户端,我们可以使用redis的sortedset,也可以使用我们今天要说的rabbit的消息优先级属性 Producer代码 ...
- MQ发送的消息都到了死信队列中了
MQ在发送消息的时候,设置的过期时间太短.(昨天项目上线遇到了,开发中也遇到一次.)谨记!!!
- Sprinboot 整合 RabbitMQ,RabbitMQ 消息重试机制
当消费者消费消息的时候,出现错误,RabbitMQ 本身会有
- rabbitmq~消息失败后重试达到 TTL放到死信队列(事务型消息补偿机制)
这是一个基于消息的分布式事务的一部分,主要通过消息来实现,生产者把消息发到队列后,由消费方去执行剩下的逻辑,而当消费方处理失败后,我们需要进行重试,即为了最现数据的最终一致性,在rabbitmq里,它 ...
- RocketMQ 原理:消息存储、高可用、消息重试、消息幂等性
目录 消息存储 消息存储方式 非持久化 持久化 消息存储介质 消息存储与读写方式 消息存储结构 刷盘机制 同步刷盘 异步刷盘 小结 高可用 高可用实现 主从复制 负载均衡 消息重试 顺序消息重试 无序 ...
- RocketMQ源码 — 八、 RocketMQ消息重试
RocketMQ的消息重试包含了producer发送消息的重试和consumer消息消费的重试. producer发送消息重试 producer在发送消息的时候如果发送失败了,RocketMQ会自动重 ...
- Rocket重试机制,消息模式,刷盘方式
一.Consumer 批量消费(推模式) 可以通过 consumer.setConsumeMessageBatchMaxSize(10);//每次拉取10条 这里需要分为2种情况 Consumer端先 ...
- 消息队列RabbitMQ(五):死信队列与延迟队列
死信队列 引言 死信队列,英文缩写:DLX .Dead Letter Exchange(死信交换机),其实应该叫做死信交换机才更恰当. 当消息成为Dead message后,可以被重新发送到另一个交换 ...
- RocketMQ-消费重试机制
介绍: RocketMQ的消息重试及时分为两种,一种是Producer端重试,一种是Consume端重试. 1.Producer端重试 : 1.1消息发没发成功,默认情况下是3次重试. 2.Consu ...
- 【RocketMQ】消息的消费
上一讲[RocketMQ]消息的拉取 消息消费 当RocketMQ进行消息消费的时候,是通过ConsumeMessageConcurrentlyService的submitConsumeRequest ...
随机推荐
- Vue基础系列文章11---router基本使用
1.系统中引入路由js文件,加两个连接,分别到用户管理和用户注册页面 <router-link to="/user">用户列表</router-link> ...
- 根据TxID获取上链数据
根据TxID获取上链信息 前段时间应甲方爸爸的要求,需要在现有的业务系统中新增一个根据TxID来查询上链信息的接口.搜了一圈发现相关的信息很少,最后只能祭出终极大招:Read Source Code. ...
- [vue] 脚手架笔记
笔记 脚手架文件结构 ├── node_modules ├── public │ ├── favicon.ico: 页签图标 │ └── index.html: 主页面 ├── src │ ├── a ...
- 线程锁(Python)
一.多个线程对同一个数据进行修改 from threading import Thread,Lock n = 0 def add(lock): for i in range(500000): glob ...
- STM32CubeMX教程30 USB_DEVICE - MSC外设_读卡器
1.准备材料 正点原子stm32f407探索者开发板V2.4 STM32CubeMX软件(Version 6.10.0) keil µVision5 IDE(MDK-Arm) ST-LINK/V2驱动 ...
- DNS正向解析
实验介绍:正向解析 通常把域名到IP称为正向解析 把ip到域名称为反向解析 一:前期准备 准备一台客户端测试正向解析是否正常 修改ip 子网掩码 DNS服务器 使用VMnet8 IP要和DNS服务器端 ...
- 【Unity3D】Renderer Feature简介
1 3D 项目迁移到 URP 项目后出现的问题 3D 项目迁移至 URP 项目后,会出现很多渲染问题,如:材质显示异常.GL 渲染不显示.多 Pass 渲染异常.屏幕后处理异常等问题.下面将针对这 ...
- 【framework】View添加过程
1 前言 WMS启动流程 中介绍了 WindowManagerService 的启动流程,本文将介绍 View 的添加流程,按照进程分为以下2步: 应用进程:介绍从 WindowManagerImpl ...
- 使用docker stack方式部署web集群
如何部署swarm集群,请参考: https://blog.csdn.net/IndexMan/article/details/102713777 创建文件夹 mkdir -p /opt/docker ...
- cf思维题
1.B. Paranoid String 题意:操作一:01可以变成1,操作二:10可以变成0.给定一个串,判断字串经过若干次操作,能否长度变成1,统计数量. 思路:对01来说,1可以吃掉0,然后前边 ...