今天这是MQ消息消费必问面试的第三个问题,重复消费。如果你还没有看消息丢失、消息堆积,可以先去点击链接进行查看。
首先我们先看一下消息重复消费的定义,就是字面意思,一条消息被消费执行了多次。可以是一个消费者消费了多次,也可以是多个消费者消费了同一条消息。
一、重复消费消息对系统的影响
消息重复消费可能导致数据在数据库中被重复插入,这其实还好,重复插入只要不是影响业务,在查询时去重也可以解决,俗称治标不治本。
如果是通知类消息,用户可能会收到多条通知,对于用户的体验会有所影响。
但是消息的重复消费最坏的就是导致数据的不一致,如果是订单类的系统,多次消费带来的不一致可能是致命的错误,例如多次扣款、超卖等。
消息重复消费在系统高并发的时候会严重的影响系统性能,消费者吞吐量下降造成消息堆积等。
二、消息发生重复消费的原因
消息重复消费产生的原因有很多,比如上一篇文章消息堆积中产生消息堆积的原因之一,消费者异常多次重试消费。
- 消费者在消费消息的过程中,业务代码异常一直重试,没有对异常行为进行控制造成单条消息卡住,一直重复消费。
- 消费者应答机制不合理,在消费者处理完业务之后无法正常应答或者因网络原因应答失败。
- 配置错误,手动ack,消费者消费完成之后没有成功ack。
三、消息重复消费解决方案
- 合理配置应答机制,手动应答,消费者业务处理尽量简单,对各种异常做好处理,增强应用系统健壮性。
- 消息重试次数增加限制,防止消息一直重试。
- MQ中配置合理的消息语义,保证至少一次,然后业务端做好去重。
- 增加消息唯一标识,消费过的消息不在进行处理。
- 消费端业务逻辑处理幂等,保证不管消费多少次都与消费一次的结果相同。
- 使用死信队列,当消息一直重复消费时加入死信队列。
- 监控,发现异常消息消费及时告警。
总结
消息重复产生的原因可以概括为两点,应答失败与业务异常循环消费单条消息。
对于消息的重复消费,业界用的最多的,还是消息唯一标识加幂等。消费端代码尽可能的对异常情况做好处理,保证在发生异常之后可以正确的应答。
对于消息唯一标识这里简单说两句,可以生产者加入消息唯一标识,消费者也加入唯一标识,生产者与消费者不必相同,保证消息唯一即可。
在消费者进行消费消息时,首先根据消息唯一标识判断是否已经消费过,可以使用 Redis 或者 MySQL 中的唯一索引等方式,然后来判断该消息是否可以被消费。