Bootstrap

RabbitMQ高级篇之MQ可靠性 Lazy Queue

数据持久化的背景和挑战

持久化确保了即使 RabbitMQ 崩溃或重启,消息仍然不会丢失。同时,消息不会因为内存溢出而触发 paged out 操作,从而避免了性能下降。

但是,持久化也带来了一个问题:每条消息必须保存到内存和磁盘中,这导致了写入操作的延迟,从而降低了系统的整体并发能力。



引入惰性队列(Lazy Queue)

为了弥补这一性能损失,RabbitMQ 从 3.6 版本 引入了 惰性队列(Lazy Queue) 模式。惰性队列的目标是:

  • 提升并发能力,同时确保消息不会丢失。
  • 减少内存占用,避免内存满导致的阻塞。



惰性队列的特点

惰性队列有两个显著特点:

  1. 直接将消息写入磁盘,而不是先写入内存
    • 传统队列会首先将消息保存到内存中,然后根据是否需要持久化决定是否将其写入磁盘。
    • 惰性队列则直接将消息写入磁盘,而不经过内存。这减少了内存压力和避免了因内存满导致的系统阻塞(如 page out)。
  2. 磁盘写入优化
    • 惰性队列通过优化磁盘写入操作,提高了写入效率,比传统的持久化方式要高效得多。



惰性队列的潜在问题

尽管惰性队列解决了内存占用和阻塞问题,但也引入了新的挑战:

  • 消费者读取延迟:消息被直接写入磁盘,消费者在读取时需要将消息从磁盘加载到内存,再投递给消费者。如果消费者的处理速度很快,但磁盘读取速度较慢,可能会影响消费者的处理速度。

为了解决这一问题,RabbitMQ 进行了进一步优化:

  • 动态缓存机制:RabbitMQ 会根据消费者的处理速度动态调整消息的加载方式。
    • 如果消费者处理消息的速度较慢,RabbitMQ 直接从磁盘加载消息。
    • 如果消费者处理消息的速度较快,RabbitMQ 会提前将部分消息加载到内存中(最多 2048 条),避免磁盘读取的瓶颈影响消费者的处理速度。



RabbitMQ 中的惰性队列实现

  • RabbitMQ 3.12 版本 开始,所有队列默认都采用惰性队列模式,无法更改为传统队列模式。
  • 3.8 版本 及以下的 RabbitMQ 中,若要使用惰性队列,需要手动配置。

如何创建惰性队列(Lazy Queue)

在 RabbitMQ 中,可以通过控制台、代码或注解来创建惰性队列。

在控制台中创建惰性队列:

  1. 创建一个队列时,勾选 Lazy Mode,系统会自动将参数 x-queue-mode=lazy 加入队列的 arguments 中。



通过代码创建惰性队列:

  • 使用 QueueBuilder 来声明队列时,调用 lazy() 方法来启用惰性队列模式。例如:

@Bean
public Queue lazyQueue() {
    return QueueBuilder
            .durable("lazy.queue")
            .lazy()	// 开启Lazy模式
            .build();
}

通过注解创建惰性队列:

  • 使用 @Queue 注解时,添加 arguments 参数来设置 x-queue-mode=lazy,从而声明惰性队列。例如:

@RabbitListener(queuesToDeclare = @Queue(
    name = "lazy.queue",
    dueable = "true",
    arguments = @Argument(name = "x-queue-mode", value = "lazy")
))
public void listenLazyQueue(String msg) {
    log.info("接收到 lazy.queue 的消息:{}", msg);
}

惰性队列的性能测试

  • 在实际测试中,通过发送 100 万条消息 到惰性队列,可以看到处理的速度非常快,每秒处理 4 万到 5 万条消息,而且处理时间保持稳定,性能高于传统持久化队列。
  • 相比传统队列,惰性队列避免了消息在内存中的存储,减少了内存压力,避免了内存满导致的 paged out 和阻塞问题。



惰性队列的优势

  • 保证消息不丢失:即使 RabbitMQ 崩溃或重启,惰性队列依然能保证消息的持久性。
  • 提升并发能力:由于不再需要将消息先存储到内存,写入磁盘的效率得到提升,从而增强了队列的处理能力。
  • 避免内存阻塞:不会出现因为内存满导致的消息阻塞和系统停顿。



惰性队列的适用场景

  • 适合高吞吐量且对延迟要求不高的场景。惰性队列通过减少内存的使用,并且优化磁盘写入,显著提升了消息队列的并发能力和稳定性,适用于大量消息处理的系统。
  • 对于一些高并发的应用场景,使用惰性队列能够更好地处理负载,提高系统的响应能力。

小结

  • 惰性队列(Lazy Queue)是 RabbitMQ 中为了解决持久化队列带来的性能问题而引入的新队列模式。
  • 通过直接将消息写入磁盘、避免写入内存和内存溢出,惰性队列不仅保证了消息不丢失,还提升了并发处理能力。
  • 在使用 RabbitMQ 时,如果对消息吞吐量和并发能力有较高要求,建议使用惰性队列模式。

关键点总结

  1. 惰性队列(Lazy Queue):直接将消息写入磁盘,避免内存占用,提高并发能力。
  2. 磁盘写入优化:相比传统持久化队列,惰性队列在写磁盘时更加高效,减少了 page out 阻塞问题。
  3. 消费者动态缓存:根据消费者处理速度调整内存缓存,优化读取性能。
  4. 使用方式
    • 控制台:勾选 Lazy Mode
    • 代码:使用 lazy() 方法。
    • 注解:通过 arguments 配置 x-queue-mode=lazy
;