程序员过关斩将--真的可以用版本号的方式来保证MQ消费消息的幂等性?

灵魂拷问

  • MQ消息的消费为什么有时候要求幂等性?

  • 你们都说可以用版本号来解决幂等性消费?

  • 什么才是消息幂等性消费的根本性问题?

随着系统的复杂性不断增加,多数系统都会引入MQ来进行解耦,其实从引入MQ的初衷来说,多数系统是为了解耦多个模块带来的复杂性,而有些“架构师”却说的:为了解决性能问题。。。当然我不排除MQ有流量削峰的作用,我只是说大部分系统引入MQ最初的初衷应该是系统解耦

当一个大的单体系统逐渐被拆分为多个小系统,也就是所谓的微服务拆分之后,无论是微服务之间的通信,还是分布式事务,几乎都需要MQ的支持,这也充分体现了分布式系统中MQ的重要性。这个时候整个系统间的交互就类似于下图所示

image

生产消息

既然引入了MQ这个组件,必然意味着同时存在消息的生产者和消费者,这也是典型的订阅模式。在消息数据的整个生命周期中,会依次经过生产者=》MQ=》消费者,三个主要部分。在生产者角度,消息的可靠投递是首要的任务,由于网络的不可靠性,所以消息理论上是不可能100%都投递成功的,针对这种情况,一般的解决方案就是消息重传

当然重传机制并非无限制的重传,可以根据业务制定具体的重传策略,比如:可以设置最大重传次数为10次,而重传的时间间隔依次增加。这种方案虽然简单,但是带来的副作用就是消息重复投递的问题。

为什么需要幂等性消费

幂等是一个数学上的概念理论,它的意思是多次执行同一个操作和执行一次操作,最终得到的结果是相同的。

举一个业务不恰当但是很准确的栗子:你的女朋友出轨一次和出轨多次,对于你来说,结果其实是一样的:你被绿了。所以出轨一次和出轨多次的结果对于你来说是相同的。

对于MQ来说,退一万步讲,就算MQ的消息无重复投递的问题,在消费端的业务中,那些对于消息消费敏感的业务,我们在设计程序架构的时候也要把消息的幂等性消费考虑在其中,比如:用户购买商品赠送红包或者积分的业务场景,这样的场景对于消息的重复消费很敏感,如果程序处理不当,出现重复给用户送红包的情况,估计程序员又要背锅来祭天了。

幂等性其实很好做

任何业务场景接口的幂等性设计,都要找出幂等性产生的数据标识。

MQ消息的重复性问题,从消息的整个流转过程来看,大体上可以在两个方向来解决:

  • 消息产生的时候避免投递重复性消息,既:消息生产者来保证消息唯一性

  • MQ本身提供重复消息的过滤功能

  • 消息被消费的时候避免被重复消费

image

在消息被消费之前的前半部分流程中,生产者可以利用唯一的消息id和ACK机制来做消息被重复投递的保证工作,但是这样会大大降低生产者业务的性能,一般情况下生产者都需要异步的来发送MQ消息,如果在发送的时候还需要检查消息是否被发送过,这无疑不是一个好的设计,而且你这样做的检查效果,只为命中很渺小的一部分数据,得不偿失,所以在生产者很少有人主动去做消息的重复投递检查工作

至于在MQ的内部,有的MQ确实会提供幂等性的存储设计,比如Kafka引入了Producer ID(即PID)和Sequence Number。

  • PID。每个新的Producer在初始化的时候会被分配一个唯一的PID,这个PID对用户是不可见的。

  • Sequence Numbler。(对于每个PID,该Producer发送数据的每个<Topic, Partition>都对应一个从0开始单调递增的Sequence Number。

Broker端在缓存中保存了这seqnumber,对于接收的每条消息,如果其序号比Broker缓存中序号大于1则接受它,否则将其丢弃。这样就可以实现了消息重复提交了。但是,只能保证单个Producer对于同一个<Topic, Partition>的Exactly Once语义。不能保证同一个Producer一个topic不同的partion幂等。

然而这些都不是我们今天要说的重点,实际的业务中,消息的幂等性消费也更倾向于在消费端做,在消息的终点彻底解决问题,无论是在系统设计,还是在可扩展性上无疑都是最好的。

刚才也提到,消息既然要做到幂等性消费,必须要提供一个用于判断重复的标识,可以是自定义的消息ID,也可以是消息中几个字段联合起来的类似数据表中的主键,目前主流的做法是在生产方根据业务特点生成消息id,例如:给用户添加因为下单而赠送积分的消息id,就可以根据userid_orderId_积分数量来生成唯一的消息id。

有了唯一的消息id,消费者就可以把已经消费的消息id,本地存储下来用于过滤重复消息,当然如果数据量比较大的话,很早之前的历史数据完全可以删除或者转移到其他的备份表,毕竟同一个消息不可能过了很长时间再次被投递。以下是一个本地消息表的例子:

字段说明
MsgId消息id
CreateTime创建时间
...其他有用的业务字段

当消费新消息的时候,执行以下类似以下的sql语句,拿到消息是否已经消费过的结果来判断当前消息是否需要重复消费

select count(0) from table where MsgId='消息id'

当然,这里还会有问题,如果只有一个消费者进行消费,不会有任何问题,如果有多个消费者在并行的进行消费,在判断重复消息的时候你会需要锁来保证同样数据的顺序化,这个时候你可能需要分布式锁。

郑重提示

除了生成消息id这种方式之外,网上有很多文章指出可以利用版本号来解决幂等性问题,试问:这种方案又有多少人亲自实践过?今天我们就以给用户添加积分这个案例来庖丁解牛一下这个方案的做法:

  • 用户的积分表中需要添加版本号(Version)字段

  • 消息的生产者在消息投递中添加版本号字段

  • 消费者根据消息的版本号来执行sql具体的sql类似:

update user set amount=amount+10 ,version=version+1 where userid=100 and version=1

其实这是乐观锁的思想,关于乐观锁可以查看之前的一篇文章:

数据库的乐观锁和悲观锁并非真实的锁

对于同一条消息的重复投递来说,这样做确实可以做到幂等性消费,毕竟程序利用数据库的锁机制来保证了一致性。那有什么问题呢?

消息的版本号问题

所有的分布式系统都面临着同样的问题,就是数据的一致性问题,MQ的消费场景也不例外。以上边用户加积分为案例,因为消息的生产者在投递消息的时候需要查询当前的版本号,类似于以下sql

select version from table where userid=100

当查询到版本号信息自后,会把版本号作为消息体的一部分投递到MQ,那在并发的情况下会发生什么情况呢?假设当前的版本号为1:

线程A查询版本号为1,然后投递了版本号为1,消息id为x的消息,于此同时线程B也查询了当前用户版本,数值也为1,然后投递了消息id为Y的消息,这个时候消费端无论是先消费消息X还是消息Y,数据库的版本号都会增加,则导致了另外一个消息由于版本号的不符而消费失败。

image

这个问题能不能解决呢?欢迎在留言区留下你的解决方案,让我们一起来提高自己

END

更多精彩文章

  • ????分布式大并发系列

  • ????架构设计系列

  • ????趣学算法和数据结构系列

  • ????设计模式系列

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

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

相关文章

spring的钩子_spring提供的钩子,你知道哪些

俗话说得好“工欲善其事必先利其器”&#xff0c;现如今springboot与springcloud已成为快速构建web应用的利器。作为一个爪洼工程师&#xff0c;知道如下的spring扩展点&#xff0c;可能会让你编写出扩展性、维护性更高的代码。spring提供的钩子&#xff0c;你知道哪些bean的生…

.Net 5性能改进

起因在.Net Core跳过4.0,避免和先.Net Framework 4.0同名,版本号变为5.0,同时也不在叫.Net Core改为.Net 5(统一的叫法),先看看官方对.Net版本规划.本文主要是根据https://devblogs.microsoft.com/dotnet/performance-improvements-in-net-5/ 翻译而来.不完全翻译.顺序也有所调…

开放数字世界中的复杂图数据挑战 —— 以教育与开源场景为例

摘要&#xff1a;开源开放的数字世界开始成为时代的潮流&#xff0c;云原生、数据中台、智能PRA开始成为数字世界中的新一代中流砥柱。随着第四范式的普遍流行&#xff0c;各个行业中的数字化转型都会带了海量的具有无限关联的复杂图数据。本报告将以教育与开源两个场景为例&am…

在IIS中部署SPA应用,多么痛的领悟!

目前公司的Web项目是SPA应用&#xff0c;采用前后端分离开发&#xff0c;所以有时也会倒腾Vue框架。“前后端应用最终以容器形态、在k8s中部署, 为此我搭建了基于Gitlab flow的Devops流程。在Devops实践中&#xff0c;容器部署成为良方和事实标准。但是在开发和自测阶段&#x…

mysql闪回工具下载_MySQL闪回工具之myflash 和 binlog2sql

实践利用binlog2sql查询两个binlog之间的SQL&#xff1a;必须是两个binlog日志&#xff0c;指定start-file和stop-filebinlog2sql -h127.0.0.1 -P3309 -udba -pxxxxxx -dsakila -t employee --start-filemysql-bin.000112 --stop-filemysql-bin.000113 > /tmp/db.sql利用bin…

MySQL大表优化方案

背景阿里云RDS FOR MySQL&#xff08;MySQL5.7版本&#xff09;数据库业务表每月新增数据量超过千万,随着数据量持续增加,我们业务出现大表慢查询,在业务高峰期主业务表的慢查询需要几十秒严重影响业务方案概述一、数据库设计及索引优化MySQL数据库本身高度灵活&#xff0c;造成…

使用Azure静态Web应用部署Blazor Webassembly应用

上一次演示了如何使用Azure静态web应用部署VUE前端项目&#xff08;使用Azure静态web应用全自动部署VUE站点&#xff09;。我们知道静态web应用支持VUE&#xff0c;react&#xff0c;angular等项目的部署。除了支持这些常见前端框架&#xff0c;静态web应用同样支持微软推出的最…

TIOBE 11 月榜单:Python 挤掉 Java,Java的下跌趋势确立了?

喜欢就关注我们吧&#xff01;TIOBE 公布了 2020 年 11 月的编程语言排行榜。Python 已成功跃居榜单第二名&#xff0c;本月排名率为 12.12%&#xff1b;Java 被挤到第三位&#xff0c;排名率降至 11.68%。自有 TIOBE 榜单以来&#xff0c;C 和 Java 之前一直占据着前两名的位置…

一路踩坑,被迫聊聊 C# 代码调试技巧和远程调试

一&#xff1a;背景 1. 讲故事每次项目预交付的时候&#xff0c;总会遇到各种奇葩的坑&#xff0c;我觉得有必要梳理一下以及如何快速解决的&#xff0c;让后来人避避坑&#xff0c;这篇就聊聊自己的所闻所遇&#xff1a;我去&#xff0c;本地环境代码跑的哧溜&#xff0c;上了…

mysql decimal型转化为float_5分钟搞懂MySQL数据类型之数值型DECIMAL类型

速成指南5分钟搞懂MySQL数据类型之数值型--DECIMAL类型DECIMAL类型的语法&#xff1a;DECIMAL[(M[,D])] [UNSIGNED] [ZEROFILL]。其中M指定的是数字的总位数(精度&#xff0c;最大65&#xff0c;默认值10)&#xff0c;D指定的是小数点后数字的位数(最大30&#xff0c;并且不能大…

Java面试必问JVM调优,那.NET5呢?

JVM调优已经是普通Java工程师的必修课了&#xff0c;而.NET开源快5年了&#xff0c;CLR层面的优化到目前都不多见&#xff0c;甚至常用的性能调优工具都还没玩过。.NET5马上来了&#xff0c;要想在互联网大潮中逆袭&#xff0c;光靠平台是不够的&#xff0c;开发者也得给力才行…

win10新建管理员账户_【经验篇001】Win10专业版如何开启超级管理员账户

关注我们前言介绍我们在使用Win10系统的时候&#xff0c;有时候安装一些特殊的专业类软件&#xff0c;需要系统赋予软件特殊的权限&#xff0c;那就需要使用超级管理员帐户&#xff0c;Win10系统安装时候&#xff0c;Administrator账户默认是禁用的&#xff0c;所以我们就需要开…

SQL Server in Docker - 还原数据库

SQL Server in Docker 还原数据库上一回演示了如果在Docker环境下安装SQL Server(使用Docker运行SQL Server)&#xff0c;这次我们来演示下如何还原一个数据库备份文件到数据库实例上。使用winscp上传bak文件到linux服务器上一回我们启动docker容器的时候使用了-v参数挂账了本地…

Xamarin 从零开始部署 iOS 上的 Walterlv.CloudKeyboard 应用

本文将告诉大家如何从零开始在 iOS 上部署 Walterlv.CloudKeyboard 应用。这个 Walterlv.CloudKeyboard 应用是一个云输入法应用&#xff0c;在 GitHub 完全开源&#xff0c;采用 Xamarin 开发&#xff0c;用途是让手机接收电脑端的打字输入的输入法。因为我没有在 iOS 上找到任…

Win10 Terminal + WSL 2 安装配置指南,精致开发体验

自从 Windows Terminal 正式发布后就再没有用过 Windows 系统自带的终端了。主要是 Terminal 简洁且灵活&#xff0c;更重要的是支持特殊字体&#xff0c;通过一些简单的配置可以使得终端看起来更舒适养眼。自从 Win 10 有了 Linux 子系统&#xff08;WSL&#xff09;&#xff…

mysql数据转储方法_Mysql数据库各种导出导入数据方式的区别(我的理解错误还望指正)...

mysqldump,NAVICAT转储&#xff0c;select * outfile在千级数据&#xff0c;万级&#xff0c;百万级数据下的表现。千级数据mysqldump导出sql文件导出是出了拒绝访问的错误&#xff1b;为对应目录(.sql文件要保存的目录)的对应用户添加(正在使用的用户)添加写入权限即可。mysql…

JetBrains 开发者调查 - 编程语言趋势

几个月前在公众号里发布了 StackOverflow 2020 开发者调查结果&#xff0c;其结果对 .NET Core 很友好。今天我们看看 JetBrains 2017-2020 四年的开发者调查结果统计&#xff0c;JetBrains 是偏 Java 系的&#xff0c;尤其是本家的 Kotlin 语言。 我们看一下在编程语言方面的趋…

mycli mysql_MyCLI :易于使用的 MySQL/MariaDB 客户端

导读MyCLI 是一个易于使用的命令行客户端&#xff0c;可用于受欢迎的数据库管理系统 MySQL、MariaDB 和 Percona&#xff0c;支持自动补全和语法高亮。它是使用 prompt_toolkit库写的&#xff0c;需要 Python 2.7、3.3、3.4、3.5 和 3.6 的支持。MyCLI 还支持通过 SSL 安全连接…

究竟是什么可以比反射还快实现动态调用?

戏精分享 C#表达式树&#xff0c;第一季正式完稿 前不久&#xff0c;我们发布了《只要十步&#xff0c;你就可以应用表达式树来优化动态调用》。观众们普遍反映文章的内容太多复杂不太容易理解。因此&#xff0c;我们以此为契机发布了《戏精分享 C#表达式树》系列视频。现在&am…

BCVP,想真正为社区做努力的开发者们

基于Net/Core&#xff0c;快速搭建 API & SPA 及微服务应用组织BASE NETCORE (VUE) PROJECT TEAM每一个.NET开发者都可以通过自己的开源项目(最好可以配套简单发表些文章)在这里进行分享&#xff0c;BCVP开发者组织的意义就是激发和挖掘更多的作品&#xff0c;可能偏基础&a…