一、幂等性是什么?
在编程中,一个幂等操作的特点是其任意多次执行所产生的影响均与一次执行的影响相同。幂等函数,或幂等方法,是指可以使用相同参数重复执行,并能获得相同结果的函数。这些函数不会影响系统状态,也不用担心重复执行会对系统造成改变。简单来说就是用户对于同一操作发起的一次请求或者多次请求的结果是一致的。
二、什么情况下会出现重复消费?
为了保证消息必达性,MQ使用了消息超时、重传、确认机制,使得消息可能被重复发送,从而对业务产生影响。
1、生产者已把消息发送到mq,在mq给生产者返回ack的时候网络中断,故生产者未收到确定信息,生产者认为消息未发送成功,但实际情况是,mq已成功接收到了消息,在网络重连后,生产者会重新发送刚才的消息,造成mq接收了重复的消息。
2、消费者在消费mq中的消息时,mq已把消息发送给消费者,消费者在给mq返回ack时网络中断,故mq未收到确认信息,该条消息会重新发给其他的消费者,或者在网络重连后再次发送给该消费者,但实际上该消费者已成功消费了该条消息,造成消费者消费了重复的消息。
三、防重复消息幂等性解决方案
1、生产者重复发消息MQ幂等性设计
MQ在接收生产者传来的消息时,MQ内部会生成一个全局唯一与业务无关的消息ID:inner-msg-id。当MQ-server接收到消息时,先根据inner-msg-id判断消息是否重复发送,再决定是否将消息落地到DB或者文件中。这样,有了这个inner-msg-id作为去重的依据就能保证一条消息只能一次落地到磁盘中。
2、消费者接收重复消息幂等性解决方案
- 利用数据库唯一性约束去实现幂等性
创建消息去重表,把全局唯一ID做为主键,做唯一性约束,如果插入成功就表示没有消费过这条消息,可以进行消费了,插入失败表示消息已经被消费了。
- 利用Redis的原子性去实现幂等性
我们都知道redis是单线程的,并且性能也非常好,提供了很多原子性的命令。比如可以使用 setnx 命令。在接收到消息后将消息ID作为key执行 setnx命令,如果执行成功就表示没有消费过这条消息,可以进行消费了,执行失败表示消息已经被消费了。
- 利用zookeeper分布式锁实现幂等性
在接收到消息后用消息ID来获取这个锁的客户端,最终只有一个可以成功获得这把锁。通常的做法是把 zk 上的一个 znode 看作是一把锁,通过 create znode 的方式来实现。所有客户端都去创建 /lock/xxxxx 节点,最终成功创建的那个客户端就表示没有消费过这条消息,可以进行消费了,创建不成功的节点会收到异常的提示,表示消息已经被消费了。
总结:三种幂等性实现方案从性能上比较:redis--》zookeeper--》数据库性。
从安全性比较:zookeeper--》数据库--》redis
程序秘籍