RabbitMQ–基础–11.2–幂等性,惰性队列
1、幂等性
就是重复消费消息
1.1、消息重复消费
消费者在消费MQ中的消息时,MQ已把消息发送给消费者,消费者在给MQ返回ack时网络中断,故MQ未收到确认消息,该条消息会重新发给其它的消费者,或者在网络重连后再次发送给该消费者,但实际上该消费者已成功消费了该条消息,造成消费者消费了重复的消息。
1.2、消费端的幂等性保障
在海量订单生成的业务高峰期,生产端有可能就会重复发送了消息,这时候消费端就要实现幂等性,这就意味着我们的消息永远不会被消费多次,即使我们收到了一样的消息。
1.2.1、业界主流的幂等性有两种操作
- 唯一ID+指纹机制,利用数据库主键去重
- 利用redis的原子性来实现
1.2.2、唯一ID+指纹机制
1.2.1.1、指纹码
我们的一些规则或者时间戳加别的服务给到的唯一信息码,它并不一定是我们系统生成的,基本都是由我们的业务规则拼接而来,但是一定要保证唯一性,然后就利用查询语句进行判断这个ID是否存在数据库中
- 优势
- 实现简单,然后查询判断是否重复
- 劣势
- 高并发时,如果是单个数据库就会有写入性能瓶颈,不是我们最佳推荐的方式。
1.2.3、利用redis的原子性
利用redis执行setnx命令,天然具有幂等性。从而实现不重复消费。
2、惰性队列
3.6.0 版本开始,新增了Lazy Queues
概念,也就是惰性队列。
2.1、特征
- 接收的消息后直接存入磁盘而非内存
- 消费者要消费消息时才会从磁盘中读取并加载到内存中
- 设计目标是能够支持更长的队列,即支持更多的消息存储。
- 支持数百万的消息存储
2.1.1、优点
基于磁盘存储,消息上限高
没有间歇性的page-out,性能比较稳定
2.1.2、缺点
基于磁盘存储,消息时效性会降低
性能受限于磁盘的IO
2.2、使用场景
解决消息堆积问题
- 当消费者由于各种各样的原因(比如消费者下线、宕机亦或者是由于维护而关闭等)而致使长时间内不能消费消息造成堆积时,惰性队列就很有必要了。
- 默认情况下,当生产者将消息发送到RabbitMQ的时候,队列中的消息会尽可能的存储在内存之中,这样可以更加快速的将消息发送给消费者。即使是持久化的消息,在被写入磁盘的同时也会在内存中驻留一份备份。当rabbitmq需要释放内存的时候,会将内存中的消息换页至磁盘中,这个操作会耗费较长的时间,也会阻塞队列的操作,进而无法接收新的消息。而惰性队列在接收消息后会直接写到磁盘,所以不会触发队列的存储预警,性能具有稳定性,延迟性。 正因为惰性队列支持百万的消息存储,所以用它来解决消息堆积问题再好不过了。
2.3、队列的两种模式
- default(默认)
- lazy:惰性队列的模式
2.4、设置队列为惰性队列的方式
- 可以通过调用channel.queueDeclare方法的时候在参数中设置
- 可以通过Policy的方式设置
- 如果一个队列同时使用这两种方式设置的话,那么Policy的方式具备更高的优先级。
- 如果要通过声明的方式改变已有对列的模式的话,那么只能先删除队列,然后再重新声明一个新的。
2.4.1、设置队列为惰性队列–直接声明
- 可以通过"x-queue-mode"参数来设置队列的模式
- 取值为
- “default”
- “lazy”
直接代码设置
Map<String, Object> arguments = new HashMap<>();
arguments.put("x-queue-mode","lazy");
channel.queueDeclare(QUEUE_NAME,false,false,false,arguments);