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 的现有特性,实现了强大的定时任务功能。

相关推荐
大G的笔记本3 小时前
redis分布式锁过期问题和自动续期和主从延迟问题
redis·分布式
隔壁寝室老吴4 小时前
使用Flink2.0消费低版本的Kafka
分布式·kafka
Chasing__Dreams6 小时前
Mysql--基础知识点--105--分布式事务
数据库·分布式·mysql
java干货7 小时前
Redis 分布式限流的四大算法与终极形态
数据库·redis·分布式
富士康质检员张全蛋7 小时前
Kafka架构 主题中的分区
分布式·kafka
富士康质检员张全蛋7 小时前
kafka 环境部署
分布式·kafka
PGFA8 小时前
【深度实战】详解 ORA-01591:因网络波动引发的分布式事务死锁及全流程修复
网络·分布式
FL4m3Y4n8 小时前
分布式消息推送系统协议设计【C++ grpc kafka】
c++·分布式·kafka
ward RINL8 小时前
分布式推理框架 xDit
分布式
HUGu RGIN8 小时前
分布式与集群,二者区别是什么?
分布式