我前几天建了一个java讨论的qq群(920868614),拉了几个人在讨论。这几天和群友聊的时候,有个群友提了一个面试问题,大家回答的时候有个群友(简哥,他qq昵称就叫简哥)提出了一个不同寻常的论点,然后就讨论了一上午的讨论。
本人技术有限无法评判对错,只能总结下群友(简哥)和其他群友的观点发上来。
面试官:如何解决消息队列重复消费的?
群友:通过幂等、数据库消息表、状态机...
简哥:正常使用不会重复消费 ,直接反问他,他是怎么用的?代码是怎么写的?
我:消息队列我都没用过哎,那是啥?
简哥观点上的冲突引发了讨论,群友使用质疑,简哥质疑群友的质疑。因为是qq群聊天的形式。群友问答比较散,图不好截,聊着聊着还会偏题。 所以我大体整理下几个关键问答。
群友:正常使用也有问题,比如发送消息后消息队列接收成功,但是回告的时候出现网络波动宕机。 导致客户端没有正确接收,客户端重发消息自然会产生重复的消息。
简哥:每条消息都会有一个id或发送时间,你都重发了这个肯定是不一样的。内容不一样那怎么能说是重复消息,只能说这两条消息高度相似。
群友:可消费端接收这两条消息,比如订单扣库存场景,不就重复扣库存了嘛。
简哥:那是你没做幂等,这和消息队列没关系,就算你用接口调也会重复扣。
群友:这种减库存的怎么加幂等?
简哥: 方法其实挺多的。比如单据减库存, 在库存前面加个库存流水表。 单据先生成流水,流水去减库存。减的时候判断单据的流水是否存在。 怎么实现都行,无非就是考虑怎么适合自己系统的。
群友:那消费的时候,因为宕机或者负载重设出现部分消息没有接收到消息确认通知,导致重新发送一次消息,这总是重复了吧。
简哥:那你这么说的话确实是重复了。 但我个人建议,系统不是那种百万级别并发的时候,不要去考虑这种情况。一般来讲这种情况的解决方案也可以用上面说的幂等和合理的业务设计去解决。
简哥:但如果系统并发太大的话,比如还是我上面说的幂等判断,他的单条链路就变成:判断流水不存在-> 新增流水-> 扣减库存。在执行过程中(未提交事务)这时候有条重复的消息进来,流水不存在,就可以绕过幂等。 导致库存重复扣。
群友: 加锁呀,库存金额这种比较重要,一般不都是加锁的。redis的分布式锁。
简哥:可以是可以,但是到这种体量的时候redis也不可靠,redis分布式锁是互斥的,会极大的影响并发。最重要的是,库存容易乱序,因为redis的分布式锁是无序的。
群友: 换数据库加乐观锁是不是可以解决, update 后面跟where条件的那个。
简哥:还记得我们的前提嘛,现在是百万并发的大型系统。但凡大点的系统,性能瓶颈都在数据库上。因为这个完全是把压力转嫁给了数据库。 这样搞长事务会非常多,尤其是库存这种核心表。
群友: 所以要保证顺序消费的话,那其实不管用什么,性能都上不去。 所以扣库存这种业务逻辑,本身要保证顺序,只能一个一个消费了?
简哥:那也不是,看业务场景。 有些保证不能超卖,有些无所谓顺序。 有些仓库是卖螺丝的小配件基本都是称重的,他录了数量,但基本不在乎。有些卖手机的,少一台都是问题。
群友: ???
简哥:总之就是,看业务,看需求。 要是普通用用不会有什么重复消费之类的情况。顶多就是消费端上个幂等兜底就行了。但是面试官要是问下去,问清楚他们是什么架构什么场景的,针对性的去回答就好了。
其实聊到这里的时候,就没几个能接上话了,话题也差不多终结了。 我也不知道他们说得对不对,但我感觉字多的有道理。我下次面试也这么试试。
面试官: 如何解决消息队列重复消费的?
我:正常使用不会重复消费,你怎么用的?代码怎么写的?
面试官: ???
这样想想还挺带感~