RocketMQ解决幂等性问题
一.造成重复消费的原因
在于回馈机制。正常情况下,消费者在消费消息时候,消费完毕后,会发送一个ACK确认信息给消息队列(broker),消息队列(broker)就知道该消息被消费了,就会将该消息从消息队列中删除。
不同的消息队列发送的确认信息形式不同,例如RabbitMQ是发送一个ACK确认消息,RocketMQ是返回一个CONSUME_SUCCESS成功标志,kafka实际上有个offset的概念。
造成重复消费的原因?,就是因为网络原因闪断,ACK返回失败等等故障,确认信息没有传送到消息队列,导致消息队列不知道自己已经消费过该消息了,再次将该消息分发给其他的消费者。(因为消息重试等机制的原因,如果一个consumer断了,rocketmq有consumer集群,会将该消息重新发给其他consumer)
这个问题针对业务场景来答,分以下三种情况:
(1)比如,你拿到这个消息做数据库的insert操作,那就容易了,给这个消息做一个唯一的主键,那么就算出现重复消费的情况,就会导致主键冲突,避免数据库出现脏数据。
(2)再比如,你拿到这个消息做redis的set的操作,那就容易了,不用解决,因为你无论set几次结果都是一样的,set操作本来就算幂等操作。
(3)如果上面两种情况还不行,上大招。准备一个第三方介质,来做消费记录。以redis为例,给消息分配一个全局id,只要消费过该消息,将<id,message>以K-V形式写入redis.那消费者开始消费前,先去redis中查询有没有消费记录即可。
二.单机环境解决方案
生产者:发送消息同时set一个key做唯一标识
public static void main(String[] args) throws MQClientException {
DefaultMQProducer producer = new DefaultMQProducer("rmq-group");
producer.setNamesrvAddr("192.168.42.22:9876;192.168.42.33:9876");
producer.setInstanceName("producer");
producer.start();
try {
for (int i = 0; i < 1; i++) {
Thread.sleep(1000); // 每秒发送一次MQ
Message msg = new Message("itmayiedu-topic", // topic 主题名称
"TagA", // tag 临时值
("itmayiedu-6" + i).getBytes()// body 内容
);
//setKey,做唯一标识
msg.setKeys(System.currentTimeMillis() + "");
SendResult sendResult = producer.send(msg);
System.out.println(sendResult.toString());
}
} catch (Exception e) {
e.printStackTrace();
}
producer.shutdown();
}
消费者:
//保存标识的集合
static private Map<String, String> logMap = new HashMap<>(); public static void main(String[] args) throws MQClientException {
DefaultMQPushConsumer consumer = new DefaultMQPushConsumer("rmq-group"); consumer.setNamesrvAddr("192.168.42.22:9876;192.168.42.33:9876");
consumer.setInstanceName("consumer");
consumer.subscribe("itmayiedu-topic", "TagA"); consumer.registerMessageListener(new MessageListenerConcurrently() {
@Override
public ConsumeConcurrentlyStatus consumeMessage(List<MessageExt> msgs, ConsumeConcurrentlyContext context) {
String key = null;
String msgId = null;
try {
for (MessageExt msg : msgs) {
key = msg.getKeys();
//判断集合当中有没有存在key,存在就不需要重试,不存在先存key再回来重试后消费消息
if (logMap.containsKey(key)) {
// 无需继续重试。
System.out.println("key:"+key+",无需重试...");
return ConsumeConcurrentlyStatus.CONSUME_SUCCESS;
}
msgId = msg.getMsgId();
System.out.println("key:" + key + ",msgid:" + msgId + "---" + new String(msg.getBody()));
//模拟异常
int i = 1 / 0;
} } catch (Exception e) {
e.printStackTrace();
//重试
return ConsumeConcurrentlyStatus.RECONSUME_LATER;
} finally {
//保存key
logMap.put(key, msgId);
}
return ConsumeConcurrentlyStatus.CONSUME_SUCCESS;
}
});
consumer.start();
System.out.println("Consumer Started.");
}
执行效果:

三.集群环境解决方案
在生产者端要保证幂等性的话,大概可以使用以下两种方式:
① RocketMQ支持消息查询的功能,只要去RocketMQ查询一下是否已经发送过该条消息就可以了,不存在则发送,存在则不发送
② 引入redis,在发送消息到RocketMQ成功之后,向redis中插入一条数据,如果发生重试,则先去redis中查询一下该条消息是否已经发送过了,存在的话就不重复发送消息了
生产者的这两种幂等性方案都可以实现,但是都存在一定的缺陷
方案①,RocketMQ消息查询的性能不是特别好,如果是在高并发的场景下,每条消息在发送到RocketMQ时都去查询一下,可能会影响接口的性能
方案②,在一些极端的场景下,redis也无法保证消息发送成功之后,就一定能写入redis成功,比如写入消息成功而redis此时宕机,那么再次查询redis判断消息是否已经发送过,是无法得到正确结果的
既然在消费者做幂等性的方案都不是特别靠谱,那就再在消费者端来做吧
消息的消费,最后都对应的是数据库的操作,只要在消息消费的时候,判断一下数据库中是否已经消费过了这条消息,就可以保证幂等性了,例如使用唯一索引,保证一条消息只被消费一次。
参考:https://blog.csdn.net/LO_YUN/article/details/104135197
去重原则:1.幂等性 2.业务去重
幂等性:(处理必须唯一) 无论这个业务请求被(consumer)执行多少次,我们的数据库的结果都是唯一的,不可变的。
去重策略:去重表机制,业务拼接去重策略(比如唯一流水号)
1.建立一个消息表,拿到这个消息做数据库的insert操作。给这个消息做一个唯一主键(primary key)或者唯一约束,那么就算出现重复消费的情况,就会导致主键冲突。
高并发下去重:采用Redis去重(key天然支持原子性并要求不可重复),但是由于不在一个事务,要求有适当的补偿策略,但是对于很重要的业务,不应该支持补偿
2.利用redis事务,主键(我们必须把全量的操作数据都存放在redis里,然后定时去和数据库做数据同步)—-即消费处理后,该处理本来应该保存在数据库的,先保存在redis,再通过一定业务方式从redis中取数据进行db持久化
3.利用redis和关系型数据库一起做去重机制
4.拿到这个消息做redis的set的操作.redis就是天然幂等性
5.准备一个第三方介质,来做消费记录。以redis为例,给消息分配一个全局id,只要消费过该消息,将 < id,message>以K-V形式写入redis。那消费者开始消费前,先去redis中查询有没消费记录即可。
消息重复消费是一个非常常见的问题,在很多系统调用频繁的场景下,都可能会出现超时重试的情况,还有就是系统频繁迭代,经常重启系统更新的场景,也会出现消息重复消费
生产者端发送重复的消息到RocketMQ中其实问题不大,消息只是在RocketMQ中重复了,并没有影响到系统的数据,我们只需要在最后修改数据库的时候,保证好幂等性即可
RocketMQ解决幂等性问题的更多相关文章
- 【Java面试】什么是幂等?如何解决幂等性问题?
一个在传统行业工作了7年的粉丝私信我. 他最近去很多互联网公司面试,遇到的很多技术和概念都没听过. 其中就有一道题是:"什么是幂等.如何解决幂等性问题"? 他说,这个概念听都没听过 ...
- RocketMQ 解决 No route info of this topic 异常步骤
原文地址:https://blog.csdn.net/chenaima1314/article/details/79403113 rocketmq运行时提示 No route info of this ...
- Springboot整合RocketMQ解决分布式事务
直接上代码: 代码结构如下: 依次贴出相关类: DataSource1Config: package com.example.demo.config;import org.apache.ibatis. ...
- rocketmq(三 java操作rocket API, rocketmq 幂等性)
JAVA操作rocketmq: 1.导入rocketmq所需要的依赖: <dependency> <groupId>com.alibaba.rocketmq</group ...
- Rocketmq原理&最佳实践
MQ背景&选型 消息队列作为高并发系统的核心组件之一,能够帮助业务系统解构提升开发效率和系统稳定性.主要具有以下优势: 削峰填谷(主要解决瞬时写压力大于应用服务能力导致消息丢失.系统奔溃等问题 ...
- RabbitMQ消息幂等性问题
文章目录 1. 什么是幂等性?1.1 消息队列的幂等性1.2 模拟重试机制1.2.1 生产者代码1.2.2 消费者代码1.2.3 消费者 application.yml 配置2. 如何保证消息幂等性, ...
- RocketMQ 源码分析之路由中心(NameServer)
你可能没有看过 RocketMQ 的架构图,没关系,一起来学习一下,RocketMQ 架构图如下: 在 RocketMQ 中,有四个角色: Producer:消息的生产者,每个 MQ 中间件都有. C ...
- 消息队列之事务消息,RocketMQ 和 Kafka 是如何做的?
每个时代,都不会亏待会学习的人. 大家好,我是 yes. 今天我们来谈一谈消息队列的事务消息,一说起事务相信大家都不陌生,脑海里蹦出来的就是 ACID. 通常我们理解的事务就是为了一些更新操作要么都成 ...
- MQ的面试题
MQ的优点和缺点? 优点:解耦 异步,削峰 解耦: 所以需要用来解耦: 异步: 解决方法: 削峰: 解决方法是: 缺点:降低高可用性.增加系统的复杂程度.一致性问题 降低高可用的原因:系统引入的外部依 ...
随机推荐
- 通过 python 处理 email - Email via Python
Email via Python 1 MIME - Multipurpose Internet Mail Extensions SMTP - Simple Message Transport Prot ...
- Python 调用 Shell命令
python程序中调用shell命令,是件很酷且常用的事情今天来总结一下 1.使用os模块 的 system 此函数会启动子进程,在子进程中执行command,并返回comman ...
- [jQuery]jQuery链式编程(六)
链式编程 节约代码量 <button>快速</button> <button>快速</button> <button>快速</butt ...
- SpringCloud微服务:阿里开源组件Nacos,服务和配置管理
源码地址:GitHub·点这里||GitEE·点这里 一.阿里微服务简介 1.基础描述 Alibaba-Cloud致力于提供微服务开发的一站式解决方案.此项目包含开发分布式应用微服务的必需组件,方便开 ...
- CVE-2020-0618 SQL 远程代码执行
CVE-2020-0618 SQL Server远程代码执行 1.简介 SQL Server Reporting Services(SSRS)提供了一组本地工具和服务,用于创建,部署和管理移动报告和分 ...
- Python学习小记(2)---[list, iterator, and, or, zip, dict.keys]
1.List行为 可以用 alist[:] 相当于 alist.copy() ,可以创建一个 alist 的 shallo copy,但是直接对 alist[:] 操作却会直接操作 alist 对象 ...
- .NET CORE(C#) WPF简单菜单MVVM绑定
微信公众号:Dotnet9,网站:Dotnet9,问题或建议:请网站留言, 如果对您有所帮助:欢迎赞赏. .NET CORE(C#) WPF简单菜单MVVM绑定 阅读导航 本文背景 代码实现 本文参考 ...
- cesium结合geoserver实现地图空间查询(附源码下载)
前言 cesium 官网的api文档介绍地址cesium官网api,里面详细的介绍 cesium 各个类的介绍,还有就是在线例子:cesium 官网在线例子,这个也是学习 cesium 的好素材. 内 ...
- kong服务网关API
kong服务网关API pingforever关注 0.1762017.05.23 11:16:08字数 834阅读 7,367 kong简介 Kong 是在客户端和(微)服务间转发API通信的API ...
- thingsboard入坑记(一)本机编译运行
开发环境: windows10 x64 专业版 工具准备: git 2.16.2 windows命令行版 java jdk 1.8:https://www.cnblogs.com/harmful-ch ...