12. 消息队列-RabbitMQ-高可用核心机制

文章目录

  • 前言
  • [一、🛑 消费端限流 (QoS)](#一、🛑 消费端限流 (QoS))
    • [1. 为什么需要限流?](#1. 为什么需要限流?)
    • [2. 核心原理:`prefetchCount`](#2. 核心原理:prefetchCount)
    • [3. 关键前提:必须手动 ACK](#3. 关键前提:必须手动 ACK)
    • [4. Spring Boot 配置示例](#4. Spring Boot 配置示例)
  • [二、⏳ 消息过期时间 (TTL)](#二、⏳ 消息过期时间 (TTL))
    • [1. 两种设置方式](#1. 两种设置方式)
    • [2. 过期消息的去向](#2. 过期消息的去向)
  • [三、⚰️ 死信队列 (DLQ)](#三、⚰️ 死信队列 (DLQ))
    • [1. 消息成为"死信"的三种场景](#1. 消息成为“死信”的三种场景)
    • [2. 死信流转流程](#2. 死信流转流程)
  • [四、🚀 高级应用:实现延迟队列](#四、🚀 高级应用:实现延迟队列)
    • [1. 核心思想](#1. 核心思想)
    • [2. 典型场景:订单超时自动关闭](#2. 典型场景:订单超时自动关闭)

前言

限流、TTL、死信队列

构建高可用、高可靠 RabbitMQ 系统的"三驾马车"。它们分别解决了消费过载消息过期异常处理的问题。。


一、🛑 消费端限流 (QoS)

消费端限流,也称为服务质量(QoS),其核心目的是防止消费者被消息淹没

1. 为什么需要限流?

想象一个场景:你的 RabbitMQ 队列中积压了 10 万条消息,此时你启动了消费者服务。如果不限流,RabbitMQ 会瞬间将这 10 万条消息全部推送给消费者。如果消费者的处理能力有限(例如需要调用耗时的外部 API),这会导致:

  • 内存溢出 (OOM):大量消息堆积在内存中。
  • 服务假死:CPU 和线程资源被耗尽。
  • 系统崩溃:整个消费者服务宕机。

限流就是给消费者一个"安全阀",让它按照自己的能力去处理消息。

2. 核心原理:prefetchCount

RabbitMQ 通过 basic.qos 命令实现限流,其中最关键的参数是 prefetchCount

  • 含义 :它定义了每个消费者最多能同时持有(即已接收但未确认)的消息数量。
  • 工作流程
    1. 设置 prefetchCount = 5
    2. 消费者启动后,RabbitMQ 会立即推送 5 条消息给它。
    3. 消费者处理完其中 1 条并发送 ACK 确认后,RabbitMQ 才会再推送第 6 条消息给它。
    4. 这个过程持续进行,确保消费者始终只处理 5 条消息,实现了"按需拉取"。

3. 关键前提:必须手动 ACK

限流只在手动确认(Manual ACK)模式下生效! 如果使用自动 ACK,消息一旦被推送出去就被视为已消费,RabbitMQ 会继续疯狂推送,prefetchCount 设置将完全失效。

4. Spring Boot 配置示例

application.yml 中,通过以下配置即可轻松实现限流:

yaml 复制代码
spring:
  rabbitmq:
    listener:
      simple:
        acknowledge-mode: manual   # 1. 开启手动ACK
        prefetch: 10               # 2. 设置每个消费者最多预取10条消息
        concurrency: 5             # 3. 启动5个消费者线程

在这个配置下,你的应用最多可以同时处理 10 * 5 = 50 条消息,有效控制了并发量。


二、⏳ 消息过期时间 (TTL)

TTL(Time To Live)即消息的生存时间。超过这个时间的消息会被视为"过期",RabbitMQ 会将其丢弃。

1. 两种设置方式

RabbitMQ 支持两种 TTL 设置,它们可以组合使用,以较短的时间为准

  1. 队列级 TTL
  • 作用范围:对整个队列生效,队列中所有消息的过期时间都相同。
  • 设置方式 :在声明队列时,通过 x-message-ttl 参数设置,单位为毫秒。
  • 优点:性能高。RabbitMQ 只需定期检查队首消息是否过期即可。
  • 适用场景:所有消息具有相同的有效期,如"所有订单30分钟后关闭"。
  1. 消息级 TTL
  • 作用范围:对单条消息生效,每条消息可以有不同的过期时间。
  • 设置方式 :在发布消息时,通过 expiration 属性设置,单位为毫秒。
  • 缺点:性能开销较大。因为每条消息的过期时间不同,RabbitMQ 无法只检查队首,需要在消息即将被投递时才判断是否过期(懒删除)。
  • 适用场景:不同消息需要不同的有效期。

2. 过期消息的去向

默认情况下,过期消息会被直接删除。但更常见的做法是结合死信队列来处理。


三、⚰️ 死信队列 (DLQ)

死信队列(Dead Letter Queue, DLQ)是一个"避难所",用于存放那些无法正常被消费的消息。

1. 消息成为"死信"的三种场景

  1. 消费者拒收 :消费者处理消息失败,调用 basicNackbasicReject,并设置 requeue=false(不重新入队)。
  2. 消息过期:消息的 TTL 时间到期。
  3. 队列满了 :队列长度超过了设置的最大值(x-max-length),最早的消息会被挤出成为死信。

2. 死信流转流程

普通队列 → 死信交换机 (DLX) → 死信队列 (DLQ)

  1. 在声明普通队列时,通过 x-dead-letter-exchange 参数指定一个死信交换机。
  2. 当消息在普通队列中成为死信后,RabbitMQ 会将其重新发布到这个死信交换机。
  3. 死信交换机根据路由键(x-dead-letter-routing-key)将消息路由到最终的死信队列。
  4. 你可以为死信队列配置专门的消费者,用于记录日志、发送告警或进行人工干预。

四、🚀 高级应用:实现延迟队列

TTL死信队列 结合,是 RabbitMQ 实现延迟队列的经典模式。RabbitMQ 本身不直接支持延迟消息,但可以通过这个技巧来实现。

1. 核心思想

让消息在"普通队列"中等待一段时间(TTL),过期后自动转为死信,进入"死信队列",由"死信队列"的消费者进行真正的业务处理。这个"等待时间"就是延迟时间。

2. 典型场景:订单超时自动关闭

  1. 创建组件
  • 延迟队列 :一个没有消费者的普通队列,设置 x-message-ttl=1800000 (30分钟),并绑定到死信交换机。
  • 死信交换机 (DLX):一个普通的 Direct 或 Topic 交换机。
  • 死信队列 (DLQ):一个有消费者的队列,绑定到死信交换机。
  1. 工作流程
  • 用户下单后,生产者向"延迟队列"发送一条消息。
  • 由于"延迟队列"没有消费者,消息会一直等待。
  • 30分钟后,消息 TTL 过期,成为死信。
  • 死信被转发到"死信交换机",再路由到"死信队列"。
  • "死信队列"的消费者收到消息,执行业务逻辑,如"检查订单支付状态,若未支付则关闭订单"。

通过这种方式,你巧妙地利用 RabbitMQ 的现有特性,实现了强大的定时任务功能。

相关推荐
xiaohuoji1293 小时前
SpringBoot中整合RabbitMQ(测试+部署上线 最完整)
spring boot·rabbitmq·java-rabbitmq
枫叶v.3 小时前
Kafka 怎么保证消息的顺序性
分布式·kafka
yitian_hm5 小时前
深入理解 Kafka Producer 核心源码:消息发送全链路解析
分布式·kafka·linq
Dylan~~~15 小时前
深度解析Cassandra:分布式数据库的王者之路
数据库·分布式
传感器与混合集成电路18 小时前
面向储气库注采井的分布式光纤监测技术
分布式
ZTLJQ19 小时前
任务调度的艺术:Python分布式任务系统完全解析
开发语言·分布式·python
被摘下的星星19 小时前
Hadoop伪分布式集群搭建实验原理概要
大数据·hadoop·分布式
无名-CODING1 天前
Java 爬虫高级技术:反反爬策略与分布式爬虫实战
java·分布式·爬虫
8Qi81 天前
Redis哨兵模式(Sentinel)深度解析
java·数据库·redis·分布式·缓存·sentinel