1:什么是幂等性?
幂等【idempotence】是一个数学与计算机学的概念,常见于抽象代数中。在编程中一个幂等操作的特点是其任意多次执行所产生的影响均与一次执行的影响相同。
ps.幂等函数,或幂等方法是指可以使用相同参数重复执行,并能获得相同结果的函数。这些函数不会影响系统状态,也不用担心重复执行会对系统造成改变。
大白话:幂等性就是一个数据或者一个请求,给你重复来了多次,你得确保对应的数据是不会改变的,不能出错。
2:消息队列的三种消费方式
性能层层递减,可靠性逐步提升【1 -> 2 -> 3】
1. At Most once:消息至多被消费一次,消息可能会丢失,但绝不重传。
特点:吞吐量达,实现简单。消息可能会丢失
2. At lest once:消息至少被消费一次,消息可以重传,但绝不丢失。
特点:消息不会丢失,消息会出现多次投递【重复消息】
3. Excatly once:每一条消息只被传递一次
特点:消息不会丢失,消息不会重复。实现起来比较复杂,相比较下来性能不佳
3:为什么会出现重复消息呢?
正常情况下,出现重复消息的概率其实很小【程序员的第六感】
对于 Producer 来说:可能因为网络问题,Producer 重试多次发送消息,实际第一次就发送成
功,那么就会产生多条相同的消息。
对于 Consumer 来说:可能因为 Broker 的消息进度丢失,导致消息重复投递给 Consumer 。
Consumer 消费成功,但是因为 JVM 异常崩溃,导致消息的消费进度未及时同步给 Consumer 。
对于大多数消息队列,考虑到性能,消费进度是异步定时同步给 Broker
举例: kafka有一个叫做offset的概念,就是每个消息写进去,都有一个offset代表他的序号,然后consumer消费了数据之后,每隔一段时间,会把自己消费过的消息的offset提交一下,代表我已经消费过了,下次就算重启,kafka就会让消费者从上次消费到的offset来继续消费。但是万事总有例外,如果consumer消费了数据,还没来得及发送自己已经消费的消息的offset就挂了,那么重启之后就会收到重复的数据。
4:如何保证消息的幂等性?
消费者实现幂等性,有两种方式:
方式一:框架层统一封装:
消息排重唯一标识,由Producer基于消息生成唯一标识
消息排重标识的存储器:关系型数据库(排重表),KV数据库
排重逻辑:
业务逻辑执行成功:插入排重记录,并且需要让插入记录和业务逻辑在同一事务中。
业务逻辑执行失败:不能插入排重记录
方式二:业务层自己实现【建议】:
方式很多,这个和 HTTP 请求实现幂等是一样的逻辑:
乐观锁
先查询数据库,判断数据是否已经被更新过。如果是,则直接返回消费完成,否则执行消费。
更新数据库时,带上数据的状态。如果更新失败,则直接返回消费完成,否则执行消费。
分布式锁,Zookeeper 或者 Redis 实现分布式锁