如何保证消息不被重复消费?或者说,如何保证消息消费的幂等性?

news/2024/11/15 0:28:00/

回答这个问题,首先你别听到重复消息这个事儿,就一无所知吧,你先大概说一说可能会有 哪些重复消费的问题。

首先,比如 RabbitMQ、RocketMQ、Kafka,都有可能会出现消息重复消费的问题,正常。因为 这问题通常不是 MQ 自己保证的,是由我们开发来保证的。挑一个 Kafka 来举个例子,说说怎 么重复消费吧。

Kafka实际上有个offset的概念,就是每个消息写进去,都有一个offset,代表消息的序号,然后consumer消费了数据之后,每隔一段时间(定时定期),会把自己消费过的消息的offset提交一下,表示“我已经消费过了,下次我要是重启啥的,你就让我继续从上次消费到的offset来继续消费吧”。

但是凡事总有意外,比如我们之前生产经常遇到的,就是你有时候重启系统,看你怎么重启 了,如果碰到点着急的,直接 kill 进程了,再重启。这会导致 consumer 有些消息处理了,但是没来得及提交 offset,尴尬了。重启之后,少数消息会再次消费一次。

举个栗子。

有这么个场景。数据 1/2/3 依次进入 kafka,kafka 会给这三条数据每条分配一个 offset,代表这 条数据的序号,我们就假设分配的 offset 依次是 152/153/154。消费者从 kafka 去消费的时候, 也是按照这个顺序去消费。假如当消费者消费了 offset=153 的这条数据,刚准备去提交 offset 的这条数据,刚准备去提交 offset 到 zookeeper,此时消费者进程被重启了。那么此时消费过的数据 1/2 的 offset 并没有提 交,kafka 也就不知道你已经消费了 offset = 153 这条数据。那么重启之后,消费者会找 kafka 说,嘿,哥儿们,你给我接着把上次我消费到的那个地方后面的数据继续给我传递过 来。由于之前的 offset 没有提交成功,那么数据 1/2 会再次传过来,如果此时消费者没有去重 的话,那么就会导致重复消费。

如果消费者干的事儿是拿一条数据就往数据库里写一条,会导致说,你可能就把数据 1/2 在数 据库里插入了 2 次,那么数据就错啦。

其实重复消费不可怕,可怕的是你没考虑到重复消费之后,怎么保证幂等性

举个例子吧。假设你有个系统,消费一条消息就往数据库里插入一条数据,要是你一个消息重 复两次,你不就插入了两条,这数据不就错了?但是你要是消费到第二次的时候,自己判断一 下是否已经消费过了,若是就直接扔了,这样不就保留了一条数据,从而保证了数据的正确 性。

一条数据重复出现两次,数据库里就只有一条数据,这就保证了系统的幂等性。

幂等性,通俗点说,就一个数据,或者一个请求,给你重复来多次,你得确保对应的数据是不 会改变的,不能出错。

所以第二个问题来了,怎么保证消息队列消费的幂等性?

其实还是得结合业务来思考,我这里给几个思路:

  • 比如你拿个数据要写库,你先根据主键查一下,如果这数据都有了,你就别插入了, update 一下好吧。
  • 比如你是写 Redis,那没问题了,反正每次都是 set,天然幂等性。
  • 比如你不是上面两个场景,那做的稍微复杂一点,你需要让生产者发送每条数据的时候, 里面加一个全局唯一的 id,类似订单 id 之类的东西,然后你这里消费到了之后,先根据这 个 id 去比如 Redis 里查一下,之前消费过吗?如果没有消费过,你就处理,然后这个 id 写 Redis。如果消费过了,那你就别处理了,保证别重复处理相同的消息即可。
  • 比如基于数据库的唯一键来保证重复数据不会重复插入多条。因为有唯一键约束了,重复 数据插入只会报错,不会导致数据库中出现脏数据。

当然,如何保证 MQ 的消费是幂等性的,需要结合具体的业务来看。 


http://www.ppmy.cn/news/704325.html

相关文章

面试学习笔记

1.熟悉mysql 存储引擎、事务隔离级别、锁、索引,熟悉sql优化工作 mysql 主要的存储引擎是 innerdb myisam 2.熟练掌握spring、mybatis、spring cloud、spring boot 等主流java框架 3.熟练使用redis ,可以灵活运营redis的五种数据类型,熟悉redis持久化和…

输入一个字符串,输出其中每个唯一字符最后一次出现的下标

输入一个字符串,输出其中每个唯一字符最后一次出现的下标 dict1 {a:0, b:0, c:0, d:0, e:0, f:0, g:0,h:0, i:0, j:0, k:0, l:0, m:0, n:0,o:0, p:0, q:0, r:0, s:0, t:0, u:0,v:0, w:0, x:0, y:0, z:0} num 0 #用于指示字符串的字符下标def function(a, dict…

计算机蓝屏显示的英文是什么,电脑蓝屏出现一堆英文怎么解决?

01 虚拟内存是windows系统中独有的解决资源不足的方式,有些用户总是将自己的硬盘装得很满,从而导致致虚拟内存因硬盘存储空间不足出现运算错误,而产生蓝屏,这种现象只需释放你的硬盘空间,把一些不必要的文件给删除&…

面试官,不要再问我三次握手和四次挥手

温馨提示:本篇文章会长期维护及更新,详情见:https://yuanrengu.com/2020/77eef79f.html 面试相关文章推荐: 面试必备 | 小伙伴栽在了JVM的内存分配策略。。。垃圾收集器(CMS、G1)及内存分配策略Java虚拟机…

ssh服务器拒绝了密码 请再试一次

安装ssh服务: sudo apt install openssh-server 秋辰以往部署Debian系统都是使用主机商的一键安装镜像,很久没自主安装过Debian系统,闲来无事自主安装体验了一下Debian新版还遇到了不少麻烦,当我装完系统打算使用SSH工具&#x…

一次完整的http请求全过程(知识体系版)

最差的面试体验 面试官评价:除了学历,和培训班出来的没区别。 确实,很多东西我只是会用。面完回来,我发现我确实连一个完整的http请求如何发送都不明白。 失败并不可怕。但是一定得把失败的悲痛化成力量! 我觉得需…

一次关于批改网的fuzz测试

一次关于批改网的fuzz测试 前言 本学期的期中作业要求要写一篇英语作文,完了以后还要在批改网上提交,如果分数大于90分的话,好像可以得到特等奖,有两百元的奖金。我奋斗了一下午,通过句子与句子之间疯狂使用that和wh…

cron表达式每N,秒,分,小时执行一次 cron常用表达式

常用cron表达式例子 这里稍微解释几下这个地方 0/2 * * * * ? 表示每2秒 执行任务(数值不能超过60,如超过好像会按最大上限执行每60秒执行一次,即最多每60秒执行一次,错误表达0/120 * * * * ?,并不会每120秒&…