对分布式消息队列来说,同时做到确保一定投递和不重复投递是很难的,就是所谓的“有且仅有一次” 。RocketMQ择了确保一定投递,保证消息不丢失,但有可能造成消息重复。
处理消息重复问题,主要有业务端自己保证,主要的方式有两种:业务幂等和消息去重。
业务幂等:第一种是保证消费逻辑的幂等性,也就是多次调用和一次调用的效果是一样的。这样一来,不管消息消费多少次,对业务都没有影响。这可以通过在消费端使用唯一标识来实现,比如数据库表的唯一索引、分布式锁等。
消息去重:第二种是业务端,对重复的消息就不再消费了。这种方法,需要保证每条消息都有一个惟一的编号,通常是业务相关的,比如订单号,消费的记录需要落库,而且需要保证和消息确认这一步的原子性。什么是消息确认呢? 消费端在处理消息后,需要向 RocketMQ 发送消费确认。RocketMQ 会记录消费状态,如果消费成功,则标记该消息已被消费。如果消费端由于异常崩溃等原因未能发送消费确认,RocketMQ 会重新将消息投递给消费端,确保消息被正确消费。
具体做法是可以建立一个消费记录表,拿到这个消息做数据库的insert操作。给这个消息做一个唯一主键(primary key)或者唯一约束,那么就算出现重复消费的情况,就会导致主键冲突,那么就不再处理这条消息。