RabbitMQ消息幂等性问题

文章目录

      • 1. 什么是幂等性?
        • 1.1 消息队列的幂等性
        • 1.2 模拟重试机制
          • 1.2.1 生产者代码
          • 1.2.2 消费者代码
          • 1.2.3 消费者 application.yml 配置
      • 2. 如何保证消息幂等性,不被重复消费?
        • 解决方法

1. 什么是幂等性?

在编程中一个幂等操作的特点是其任意多次执行所产生的影响均与一次执行的影响相同。


HTTP方法的幂等性是指一次和多次请求某一个资源应该具有同样的副作用。幂等性属于语义范畴,正如编译器只能帮助检查语法错误一样,HTTP规范也没有办法通过消息格式等语法手段来定义它。

简之:一个请求,不管重复来多少次,结果是不会改变的。

1.1 消息队列的幂等性

如同HTTP方法的幂等性,消息队列同样会出现幂等性问题。

消费者在消费 MQ 中的消息时,MQ 已把消息发送给消费者,消费者在给 MQ 返回 ack 时网络中断,故 MQ 未收到确认信息,该条消息会重新发给其他的消费者,或者在网络重连后再次发送给该消费者,但实际上该消费者已成功消费了该条消息,造成消费者消费了重复的消息;注意,RabbitMQ 这种消息重试(补偿)机制是默认的。

所以,MQ 消费者的幂等性问题,主要在于 MQ 的重试机制,因为网络原因或客户端延迟消费导致重复消费。

那么,如何合适选择重试机制?我们来看两种情况。

情况1: 消费者获取到消息后,调用第三方接口,但接口暂时无法访问,是否需要重试?

需要重试

情况2: 消费者获取到消息后,抛出数据转换异常,是否需要重试?

不需要重试

总结:对于情况2,如果消费者代码抛出异常是需要发布新版本才能解决的问题,那么不需要重试,重试也无济于事。应该采用日志记录+定时任务 job 健康检查+人工进行补偿

1.2 模拟重试机制

我们采用一种短信消费者客户端异常的情况来模拟 RabbitMQ 的重试机制。

@RabbitListener(queues = "fanout_sms_queue")
public void process(String msg) {System.out.println("短信消费者获取生产者消息msg:" + msg);int i = 1/0;
}

如上代码,很显然会报错,一担报错生产者的消息时不会被消费的?

@RabbitListener 底层使用 AOP 进行异常通知拦截,如果程序没有抛出异常信息,那么就会自动提交事务;如果 AOP 异常通知拦截有捕获到异常信息的话,就会自动实现重试(补偿)机制,同时,这个补偿机制的消息会缓存到 RabbitMQ 服务器端进行存放,一直重试到不抛出异常为止。

1.2.1 生产者代码
@Component
public class FanoutProducer {@Autowiredprivate AmqpTemplate amqpTemplate;/*** 发送消息** @param queueName 队列名称*/public void send(String queueName) {String msg = "my_fanout_msg:" + System.currentTimeMillis();Message message = MessageBuilder.withBody(msg.getBytes()).setContentType(MessageProperties.CONTENT_TYPE_JSON).setContentEncoding("utf-8").setMessageId(UUID.randomUUID() + "").build();System.out.println(msg + ":" + msg);amqpTemplate.convertAndSend(queueName, message);}
}
1.2.2 消费者代码
@Component
public class FanoutEamilConsumer {@RabbitListener(queues = "fanout_eamil_queue")public void process(Message message) throws Exception {String revMessage = Thread.currentThread().getName() + ",邮件消费者获取生产者消息msg:" + new String(message.getBody(), "UTF-8")+ ",messageId:" + message.getMessageProperties().getMessageId();System.out.println(revMessage);}
}
1.2.3 消费者 application.yml 配置
spring:rabbitmq:####连接地址host: 127.0.0.1####端口号   port: 5672####账号 username: guest####密码  password: guest### 地址virtual-host: /admin_hostlistener:simple:retry:####开启消费者重试enabled: true####最大重试次数max-attempts: 5####重试间隔次数initial-interval: 3000server:port: 8081

我们通过 RabbitMQ 配置,增加了 RabbitMQ 重试时间以及重试次数限制,在一定程度上解决了重复消费的问题,接下来看一道常问的面试题。

2. 如何保证消息幂等性,不被重复消费?

其实,这个问题也算是 MQ 面试当中经常考察的一点,因为无论是什么 MQ 都会有这个问题。

首先通过上边我们了解了什么是“幂等性”,以及 MQ 幂等性问题的产生,所以我们要清楚为什么会出现重复性消费?在什么场景会出现重复消费?

解决方法

使用全局 MessageID 判断消费方使用同一个,解决幂等性问题。
或者使用业务逻辑保证唯一(比如订单号码)

生产者关键代码:

@Autowired
private AmqpTemplate amqpTemplate;/*** 发送消息** @param queueName 队列名称*/
public void send(String queueName) {String msg = "my_fanout_msg:" + System.currentTimeMillis();Message message = MessageBuilder.withBody(msg.getBytes()).setContentType(MessageProperties.CONTENT_TYPE_JSON).setContentEncoding("utf-8").setMessageId(UUID.randomUUID() + "").build();System.out.println(msg + ":" + msg);amqpTemplate.convertAndSend(queueName, message);
}

如上,生产者在发送消息时(convertAndSend),给消息对象设置了唯一的 MessageID,只有该 MessageID 没有被消费者标记方能在重试机制中再次被消费。

消费者关键代码:

@RabbitListener(queues = "fanout_eamil_queue")
public void process(Message message) throws Exception {String revMessage = Thread.currentThread().getName()+ ",邮件消费者获取生产者消息msg:"+ new String(message.getBody(), "UTF-8")+ ",messageId:" + message.getMessageProperties().getMessageId();System.out.println(revMessage);发送邮件的逻辑XXX
}

如上,通过 message.getMessageProperties().getMessageId() 获取 MessageID,获取的 MessageID 可以用来判断是否已经被消费者消费过了,如果已经消费则取消再次消费。

通常怎么判断呢?

比如上方是一个邮件发送的消费者,在做补偿时,假如上一步邮件发送成功了,我们会把该 ID 存至 redis中,下次再调用时,先去 redis 判断是否存在该 ID 了,如果存在表明已经消费过了则直接返回,不再消费,否则消费,然后将记录存至 redis。

我创建了一个java相关的公众号,用来记录自己的学习之路,感兴趣的小伙伴可以关注一下微信公众号哈:niceyoo

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/414176.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Centos安装JDK(java环境)

王小私下问我 centos 中 jdk 怎么安装呀,所以再次整理了这篇基础环境搭建的文章。 1、创建java目录2、下载上传jdk3、解压jdk4、配置环境变量 1、创建java目录 首先我们创建java的安装目录 cd /usrmkdir javacd java 2、下载上传jdk 我们如上在 usr 目录下创建了 ja…

工作288:根据时间戳处理接口

<template><div class"table-list-page"><div class"query-area"><el-date-pickerv-model"value1"type"daterange"range-separator"至"start-placeholder"开始日期"end-placeholder"结…

Centos7安装MySQL(多图)

文章目录一、在线安装1、替换网易yum源2、清理缓存3、下载rpm文件4、安装MySQL数据库二、本地安装1、上传MySQL安装包2、安装依赖的程序包3、卸载mariadb程序包4、安装MySQL程序包5、修改MySQL目录权限6、初始化MySQL三、启动MySQL1.1、在线安装方式启动MySQL1.2、本地安装方式…

lower_case_table_names=1 启动报错 mysql8.0

本文为采集文章&#xff0c;主博客地址&#xff1a;https://www.cnblogs.com/niceyoo 我们知道在 Linux 环境下默认是区分大小写的&#xff0c;所以我们需要改变这种默认方式&#xff0c;经过网上各种搜索后&#xff0c;基本就是清一色的修改 lower_case_table_names&#xff0…

工作292:修改父子组件传值错误

[Vue warn]: Missing required prop: “title” 在写vue项目中&#xff0c;在子组件中通过props传值的时候&#xff0c;在父组件中没有定义的话就会看到类似的报错&#xff0c; 这个意思是calendar这个组件中通过props传递一个title属性给父组件&#xff0c;并且title属性是必…

MacOS下IDEA设置智能提示不区分大小写

本文只针对&#xff0c;IDEA-2019.2.3版本 目录地址&#xff1a; Edit -> General -> Code Completion -> Match case -> 勾选去掉 截图如下&#xff1a;

博客园文章方块背景格式

有小伙伴问到方格背景的问题&#xff0c;所以写一篇文章记录我的博客园文章背景是如何制作的。 一、辅助网站1. 一键排版2. 代码主题3. 复制二、 图床设置 一、辅助网站 辅助网址&#xff1a;Md2All 作者提供了一篇帮助文章&#xff1a;玩转公众号Markdown 其实大致看完辅助网址…

day02 pycharm 安装

pycharm 是一款现在比较主流的辅助开发软件 不选择虚拟 所以选择Existing现有的 安装后只需打开当前窗口 默认的 不需要大家新的窗口 使用鼠标滚轮来实现放大缩小 使用debug模式测试代码 转载于:https://www.cnblogs.com/zhaohongyu6688/p/8962253.html

eclipse启动项目

今天做的任务不多&#xff0c;没有自己写代码&#xff0c;上午看了些文章&#xff0c;下午我司后台给配了配项目环境&#xff0c;全装C盘了。。以后有我好受的。。 看着后台操作&#xff0c;修改了N多配置&#xff0c;tomcat、redis、zkServer.、Nginx&#xff0c;navcat、ecli…

如何写一份优秀的java程序员简历

背景&#xff1a;进入第一家公司已经工作将近两年了&#xff0c;其中闲了一年&#xff0c;在准备自己的简历的时候&#xff0c;有种江郎才尽的感觉&#xff0c;不知道怎么写&#xff0c;看来平时还是要多积累多熟悉。 PS&#xff1a;这里面的分享看完还是很受用的。 简历看得比…

macos -bash: yarn: command not found/-bash: cnpm: command not found

博客主要更新地址&#xff1a;?https://www.cnblogs.com/niceyoo -bash: cnpm: command not found -bash: yarn: command not found -bash: xxxx: command not found 如上yarn/cnpm皆通用&#xff0c;前提是安装成功后报这个错误哈&#xff01; Error: EACCES: permission den…

部署项目到jetty

一、打包项目 1、在pom.xml中添加以下依赖 <dependency><groupId>org.mortbay.jetty</groupId><artifactId>jetty-plus</artifactId><version>7.0.0.pre5</version><scope>provided</scope> </dependency> <de…

maven jar包冲突的发现与解决[工具篇]

本文是我的第177篇文章。 关于jar冲突排查解决的问题&#xff0c;相信很多小伙伴也都知道有一些&#xff0c;无非就是两类&#xff1a;命令 or 工具。 命令方式比如&#xff1a; mvn dependency:tree 工具方式比如&#xff1a; Maven Helper 而今天的主角就是 Maven Helper 了。…

@Path注解

最近用到的一个项目&#xff0c;看到Controller控制层、Method方法都是通篇的Path注解&#xff0c;由于之前并没有使用过该注解&#xff0c;故记此篇。 首先看一下项目中的使用方式&#xff1a; Path("clientWeb")public class ClientWeb { POST Path("/g…

iOS的SVN

1、cornerstone2、smart svn mac &#xff08;比较好用&#xff09;3、还xcode自带的。转载于:https://www.cnblogs.com/YangBinChina/p/8971148.html

导入数据任务(id:373985)异常, 错误信息:解析导入文件错误,请检查导入文件内容,仅支持导入json格式数据及excel文件...

小程序导入&#xff0c;别人导出的数据库json文件&#xff0c;错误信息如下&#xff1a; 导入数据库失败, Error: Poll error, 导入数据任务&#xff08;id:373985&#xff09;异常&#xff0c;错误信息&#xff1a;解析导入文件错误&#xff0c;请检查导入文件内容&#xff0c…

ArrayList与String[]

不逼自己一把&#xff0c;你永远不知道什么是绝望。 今天被初学java的朋友问到了String[]跟ArrayList是不是有关系呢&#xff1f; 猜测是名称之间的联想&#xff0c;记此篇解惑。 Array英语单词里是数组、阵列的意思&#xff0c;在java中数组是这样定义的&#xff1a;一组相关类…

WebStorm取消默认style样式折叠

WebStorm取消默认style样式折叠&#xff1a; File--->Settings打开一个窗口 Edit--->CodeFolding 把HTML style attribute的前面的钩去掉[取消勾选] 右下角点击Apply--->ok

Vue中的computed属性

1、前言 本篇是Vue中最常用到的API之一computed属性&#xff0c;转载信息如下&#xff1a; 作者&#xff1a;gunelark链接&#xff1a;https://www.cnblogs.com/gunelark/p/8492468.html 2、正文 看了网上很多资料&#xff0c;对vue的computed讲解自己看的都不是很清晰&#xf…

订单管理系统(含源码)

本文demo下载和教学视频地址&#xff1a;http://www.wisdomdd.cn/Wisdom/resource/articleDetail.htm?resourceId1077 实例使用PhpMySqlhtmlcss技术实现了订单管理系统, 系统可以分为多个角色进行登录操作, 每个角色分别对应相应的功能, 实例中介绍了一个强大的工具phpStudy&a…