网站搭建的费用,接广告赚钱的平台,wordpress 数据库同步,3g版网站制作灵魂拷问MQ消息的消费为什么有时候要求幂等性#xff1f;你们都说可以用版本号来解决幂等性消费#xff1f;什么才是消息幂等性消费的根本性问题#xff1f;随着系统的复杂性不断增加#xff0c;多数系统都会引入MQ来进行解耦#xff0c;其实从引入MQ的初衷来说#xff0… 灵魂拷问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消息idCreateTime创建时间...其他有用的业务字段当消费新消息的时候执行以下类似以下的sql语句拿到消息是否已经消费过的结果来判断当前消息是否需要重复消费select count(0) from table where MsgId消息id
当然这里还会有问题如果只有一个消费者进行消费不会有任何问题如果有多个消费者在并行的进行消费在判断重复消息的时候你会需要锁来保证同样数据的顺序化这个时候你可能需要分布式锁。郑重提示除了生成消息id这种方式之外网上有很多文章指出可以利用版本号来解决幂等性问题试问这种方案又有多少人亲自实践过今天我们就以给用户添加积分这个案例来庖丁解牛一下这个方案的做法用户的积分表中需要添加版本号Version字段消息的生产者在消息投递中添加版本号字段消费者根据消息的版本号来执行sql具体的sql类似update user set amountamount10 ,versionversion1 where userid100 and version1
其实这是乐观锁的思想关于乐观锁可以查看之前的一篇文章数据库的乐观锁和悲观锁并非真实的锁对于同一条消息的重复投递来说这样做确实可以做到幂等性消费毕竟程序利用数据库的锁机制来保证了一致性。那有什么问题呢消息的版本号问题所有的分布式系统都面临着同样的问题就是数据的一致性问题MQ的消费场景也不例外。以上边用户加积分为案例因为消息的生产者在投递消息的时候需要查询当前的版本号类似于以下sqlselect version from table where userid100
当查询到版本号信息自后会把版本号作为消息体的一部分投递到MQ那在并发的情况下会发生什么情况呢假设当前的版本号为1线程A查询版本号为1然后投递了版本号为1消息id为x的消息于此同时线程B也查询了当前用户版本数值也为1然后投递了消息id为Y的消息这个时候消费端无论是先消费消息X还是消息Y,数据库的版本号都会增加则导致了另外一个消息由于版本号的不符而消费失败。image这个问题能不能解决呢欢迎在留言区留下你的解决方案让我们一起来提高自己END更多精彩文章????分布式大并发系列????架构设计系列????趣学算法和数据结构系列????设计模式系列