RabbitMQ 消息队列:从入门到Spring Boot实战

RabbitMQ 作为一款开源的、基于 AMQP(Advanced Message Queuing Protocol)协议实现的消息代理,凭借其强大的功能、灵活的路由机制以及出色的性能,在业界得到了广泛的应用。无论是处理高并发订单、异步通知、日志收集还是系统解耦,RabbitMQ 都能发挥其独特的作用。

1. RabbitMQ 核心概念

RabbitMQ 的强大功能离不开其背后一系列精心设计的核心概念。理解这些概念是掌握 RabbitMQ 的基础,也是构建高效、可靠消息系统的关键。

1.1 生产者 (Producer) 与消费者 (Consumer)

在消息队列的世界里,生产者和消费者是两个最基本的角色,它们共同构成了消息流转的起点和终点。

  • 生产者 (Producer):生产者是消息的创建者和发送者。它负责将业务数据封装成消息,并将其发送到 RabbitMQ 消息代理(Broker)。生产者通常不关心消息最终会被哪个消费者处理,它只负责将消息可靠地投递到 RabbitMQ。

  • 消费者 (Consumer):消费者是消息的接收者和处理者。它从 RabbitMQ 的队列中获取消息,并根据业务逻辑对消息进行相应的处理。一个队列可以有多个消费者,它们之间可以形成竞争关系(竞争消费者模式),也可以形成广播关系(发布/订阅模式)。

消息在生产者和消费者之间流转,实现了业务逻辑的解耦。生产者无需知道消费者是谁,消费者也无需知道消息来自哪个生产者,它们之间通过 RabbitMQ 这一中间件进行通信,大大提高了系统的灵活性和可维护性。

1.2 消息队列 (Queue)

消息队列是 RabbitMQ 的核心组件之一,它是消息的"家",所有发送到 RabbitMQ 的消息都会被存储在队列中,直到被消费者取走。队列在 RabbitMQ 中扮演着重要的角色,它确保了消息的持久化存储和有序传输。

消息队列具有以下几个重要属性:

  • 持久性 (Durability):当队列被声明为持久化时,即使 RabbitMQ 服务重启,该队列以及其中未被消费的消息也不会丢失。这对于确保消息的可靠性至关重要。在实际应用中,为了避免消息丢失,通常会将队列设置为持久化。

  • 自动删除 (Auto-delete):如果一个队列被设置为自动删除,那么当最后一个消费者断开连接后,该队列会自动被删除。这种队列适用于临时性的消息处理场景,例如某些一次性任务的队列。

  • 惰性 (Lazy):惰性队列是 RabbitMQ 3.6 版本引入的新特性。当队列被声明为惰性时,消息会尽可能地写入磁盘,而不是一直保存在内存中。这有助于减少内存消耗,尤其是在处理大量消息堆积的场景下。但是,从磁盘读取消息会带来一定的性能开销。

  • 排他性 (Exclusive):排他队列是与连接绑定的。当声明排他队列的连接断开时,该队列会自动被删除。排他队列只能被声明它的连接访问,其他连接无法访问。这种队列通常用于客户端独占的临时队列。

1.3 交换机 (Exchange)

交换机是 RabbitMQ 消息路由的核心组件。生产者发送消息时,并不是直接将消息发送到队列,而是发送到交换机。交换机根据其类型和路由规则,将消息路由到一个或多个队列中,或者直接丢弃消息。交换机是消息路由的"交通枢纽"。

RabbitMQ 提供了四种主要的交换机类型,每种类型都有其独特的路由行为:

  • Direct Exchange (直连交换机) :直连交换机是最简单的交换机类型。它根据消息的 routing key 进行精确匹配。当消息的 routing key 与队列绑定的 binding key 完全一致时,消息才会被路由到该队列。直连交换机适用于点对点通信或需要精确路由的场景。

  • Topic Exchange (主题交换机) :主题交换机在直连交换机的基础上增加了模式匹配的功能。它允许 binding key 使用通配符进行模糊匹配。其中,* 匹配一个单词,# 匹配零个或多个单词。主题交换机非常灵活,适用于日志系统、事件分发等需要根据不同主题订阅消息的场景。

  • Headers Exchange (首部交换机) :首部交换机不依赖 routing key 进行路由,而是根据消息的 headers 属性进行匹配。生产者在发送消息时,可以在消息的 headers 中添加自定义属性。队列在绑定到首部交换机时,可以指定一组 headers 属性和匹配规则(例如 allany),只有当消息的 headers 满足匹配规则时,消息才会被路由到该队列。首部交换机提供了更灵活的路由方式,适用于复杂的业务场景。

  • Fanout Exchange (扇形交换机) :扇形交换机是最"粗暴"的交换机类型。它会将接收到的所有消息广播到所有绑定到它的队列中,完全忽略消息的 routing key。扇形交换机适用于发布/订阅模式,例如广播通知、日志分发等场景。

1.4 绑定 (Binding)

绑定是连接交换机和队列的桥梁。它告诉交换机,当满足特定条件时,应该将消息路由到哪个队列。绑定关系是 RabbitMQ 路由机制的核心,它定义了消息从交换机到队列的路径。

在创建绑定时,通常会指定一个 binding keybinding key 的作用取决于交换机的类型:

  • 对于 Direct Exchange,binding key 必须与消息的 routing key 完全匹配。
  • 对于 Topic Exchange,binding key 可以包含通配符,与消息的 routing key 进行模式匹配。
  • 对于 Fanout Exchange,binding key 会被忽略,因为所有消息都会被广播。
  • 对于 Headers Exchange,binding key 不起作用,路由基于消息的 headers

1.5 消息确认 (Acknowledgement)

消息确认是 RabbitMQ 确保消息可靠性的重要机制。当消费者从队列中获取消息并成功处理后,会向 RabbitMQ 发送一个确认(ACK)信号。RabbitMQ 收到确认信号后,才会将该消息从队列中彻底删除。如果在消费者处理消息过程中发生异常或消费者崩溃,未发送确认信号,RabbitMQ 会认为消息未被成功处理,会将消息重新投递给其他消费者(或同一消费者),从而保证消息不会丢失。

消息确认分为两种模式:

  • 自动确认 (Auto Acknowledge):在这种模式下,RabbitMQ 会在消息发送给消费者后立即将其从队列中删除,而不管消费者是否真正处理了消息。这种模式简单方便,但存在消息丢失的风险,不适用于对消息可靠性要求高的场景。

  • 手动确认 (Manual Acknowledge) :在这种模式下,消费者在处理完消息后,需要显式地向 RabbitMQ 发送确认信号。手动确认提供了更高的消息可靠性,因为只有当消费者确认消息已成功处理后,消息才会被删除。手动确认又分为肯定确认(basicAck)、否定确认(basicNack)和拒绝消息(basicReject)等操作,可以根据业务需求灵活控制消息的处理状态。

2. RabbitMQ 常用工作模式

RabbitMQ 提供了多种灵活的工作模式,以适应不同的消息通信需求。

2.1 简单模式 (Simple Mode)

简单模式,顾名思义,是最基础的消息通信模式,也被称为"Hello World"模式。它由一个生产者、一个队列和一个消费者组成。

  • 特点:生产者将消息发送到队列,消费者从队列中获取消息并处理。消息按照先进先出(FIFO)的顺序进行处理。这种模式实现了最简单的点对点通信。
  • 应用场景:适用于简单的消息传递,例如发送一次性通知、处理单个任务等。

2.2 工作队列模式 (Work Queues Mode)

工作队列模式(也称为任务队列或竞争消费者模式)旨在解决单个消费者处理消息速度慢的问题。在这种模式下,一个生产者将消息发送到一个队列,但有多个消费者同时监听该队列,共同竞争消息。

  • 特点
    • 负载均衡:RabbitMQ 默认采用轮询(Round-robin)的方式将消息分发给不同的消费者,从而实现消息的负载均衡。
    • 消息确认:为了确保消息不丢失,消费者在处理完消息后需要发送确认信号。如果消费者在处理过程中崩溃,未确认的消息会被重新投递给其他消费者。
  • 应用场景:适用于处理耗时任务的场景,例如图片处理、视频转码、邮件发送等。通过增加消费者数量,可以提高系统的吞吐量和处理能力。

2.3 发布/订阅模式 (Publish/Subscribe Mode)

发布/订阅模式(Pub/Sub)实现了消息的广播。在这种模式下,消息不再直接发送到队列,而是发送到 Fanout Exchange。所有绑定到该交换机的队列都会收到消息,然后这些队列再将消息分发给各自的消费者。

  • 特点
    • 广播:一个消息可以被多个消费者同时接收和处理。
    • 解耦:生产者和消费者之间完全解耦,生产者无需知道有多少个消费者,消费者也无需知道消息来自哪个生产者。
  • 应用场景:适用于需要将同一消息发送给多个订阅者的场景,例如日志系统(所有日志都发送给所有日志处理服务)、事件通知(用户注册成功后通知所有相关服务)等。

2.4 路由模式 (Routing Mode)

路由模式允许消息根据 routing key 进行有选择地路由。在这种模式下,消息发送到 Direct Exchange,队列在绑定到交换机时会指定一个 binding key。只有当消息的 routing key 与队列的 binding key 完全匹配时,消息才会被路由到该队列。

  • 特点
    • 选择性接收 :消费者可以根据自己感兴趣的 routing key 来接收消息。
    • 灵活路由 :通过定义不同的 routing key,可以实现消息的精细化路由。
  • 应用场景 :适用于需要根据消息的特定属性进行分类处理的场景,例如日志系统(根据日志级别路由到不同的处理服务,如 error 级别的日志发送到错误处理服务,info 级别的日志发送到信息记录服务)。

2.5 主题模式 (Topics Mode)

主题模式是路由模式的增强版,它允许 routing keybinding key 使用通配符进行模式匹配。消息发送到 Topic Exchange,队列在绑定到交换机时会指定一个包含通配符的 binding key

  • 特点
    • 更灵活的匹配* 匹配一个单词,# 匹配零个或多个单词。这使得消息路由更加灵活和强大。
    • 多条件订阅:消费者可以订阅符合特定模式的消息。
  • 应用场景 :适用于需要根据多个条件进行消息过滤和订阅的场景,例如股票行情系统(用户可以订阅 *.stock.us 来获取所有美国股票信息,或者订阅 ibm.# 来获取所有 IBM 相关的消息)。

3. Spring Boot 整合 RabbitMQ 实战

Spring Boot 极大地简化了 Spring 应用的开发,包括与各种消息中间件的集成。通过 Spring AMQP 模块,我们可以非常方便地在 Spring Boot 项目中集成 RabbitMQ,实现消息的发送和接收。

3.1 环境准备

首先,我们需要创建一个 Spring Boot 项目,并在 pom.xml 中引入 RabbitMQ 相关的 Maven 依赖。此外,确保本地或远程环境中已经安装并运行了 RabbitMQ 服务。

3.1.1 Maven 依赖引入

pom.xml 文件中添加以下依赖:

xml 复制代码
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.7.0</version> <!-- 根据您的Spring Boot版本调整 -->
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.example</groupId>
    <artifactId>rabbitmq-spring-boot-demo</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>rabbitmq-spring-boot-demo</name>
    <description>Demo project for Spring Boot and RabbitMQ</description>

    <properties>
        <java.version>1.8</java.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-amqp</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <configuration>
                    <excludes>
                        <exclude>
                            <groupId>org.projectlombok</groupId>
                            <artifactId>lombok</artifactId>
                        </exclude>
                    </excludes>
                </configuration>
            </plugin>
        </plugins>
    </build>

</project>

3.1.2 RabbitMQ 安装与配置

如果您还没有安装 RabbitMQ,可以通过 Docker 或直接下载安装包进行安装。这里以 Docker 为例,快速启动一个 RabbitMQ 实例:

bash 复制代码
docker run -it --rm --name rabbitmq -p 5672:5672 -p 15672:15672 rabbitmq:management

上述命令会启动一个 RabbitMQ 容器,并暴露 5672 端口(AMQP 协议端口)和 15672 端口(管理界面端口)。您可以通过浏览器访问 http://localhost:15672,使用默认的 guest/guest 账号登录管理界面。

3.2 配置文件

src/main/resources/application.yml (或 application.properties) 中配置 RabbitMQ 连接信息:

yaml 复制代码
spring:
  rabbitmq:
    host: localhost
    port: 5672
    username: guest
    password: guest
    virtual-host: /
    listener:
      simple:
        acknowledge-mode: manual # 消费者手动确认消息

这里我们配置了 RabbitMQ 的连接地址、端口、用户名、密码和虚拟主机。特别地,我们将消费者消息确认模式设置为 manual,以便在代码中手动确认消息,确保消息的可靠性。

3.3 生产者实现

生产者负责将消息发送到 RabbitMQ 的交换机。我们将演示如何使用 RabbitTemplate 发送消息到不同类型的交换机。

3.3.1 配置交换机和队列

为了方便管理和配置,我们可以在 Spring Boot 配置类中定义交换机、队列和绑定关系。这里以 Direct Exchange、Topic Exchange 和 Fanout Exchange 为例。

java 复制代码
package com.example.rabbitmqspringbootdemo.config;

import org.springframework.amqp.core.*;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class RabbitMQConfig {

    // Direct Exchange 相关配置
    public static final String DIRECT_EXCHANGE = "direct.exchange";
    public static final String DIRECT_QUEUE = "direct.queue";
    public static final String DIRECT_ROUTING_KEY = "direct.routing.key";

    @Bean
    public DirectExchange directExchange() {
        return new DirectExchange(DIRECT_EXCHANGE, true, false);
    }

    @Bean
    public Queue directQueue() {
        return new Queue(DIRECT_QUEUE, true);
    }

    @Bean
    public Binding directBinding(Queue directQueue, DirectExchange directExchange) {
        return BindingBuilder.bind(directQueue).to(directExchange).with(DIRECT_ROUTING_KEY);
    }

    // Topic Exchange 相关配置
    public static final String TOPIC_EXCHANGE = "topic.exchange";
    public static final String TOPIC_QUEUE_A = "topic.queue.a";
    public static final String TOPIC_QUEUE_B = "topic.queue.b";
    public static final String TOPIC_ROUTING_KEY_A = "topic.#.a"; // 匹配 topic.x.a, topic.y.z.a
    public static final String TOPIC_ROUTING_KEY_B = "topic.b.*"; // 匹配 topic.b.x

    @Bean
    public TopicExchange topicExchange() {
        return new TopicExchange(TOPIC_EXCHANGE, true, false);
    }

    @Bean
    public Queue topicQueueA() {
        return new Queue(TOPIC_QUEUE_A, true);
    }

    @Bean
    public Queue topicQueueB() {
        return new Queue(TOPIC_QUEUE_B, true);
    }

    @Bean
    public Binding topicBindingA(Queue topicQueueA, TopicExchange topicExchange) {
        return BindingBuilder.bind(topicQueueA).to(topicExchange).with(TOPIC_ROUTING_KEY_A);
    }

    @Bean
    public Binding topicBindingB(Queue topicQueueB, TopicExchange topicExchange) {
        return BindingBuilder.bind(topicQueueB).to(topicExchange).with(TOPIC_ROUTING_KEY_B);
    }

    // Fanout Exchange 相关配置
    public static final String FANOUT_EXCHANGE = "fanout.exchange";
    public static final String FANOUT_QUEUE_1 = "fanout.queue.1";
    public static final String FANOUT_QUEUE_2 = "fanout.queue.2";

    @Bean
    public FanoutExchange fanoutExchange() {
        return new FanoutExchange(FANOUT_EXCHANGE, true, false);
    }

    @Bean
    public Queue fanoutQueue1() {
        return new Queue(FANOUT_QUEUE_1, true);
    }

    @Bean
    public Queue fanoutQueue2() {
        return new Queue(FANOUT_QUEUE_2, true);
    }

    @Bean
    public Binding fanoutBinding1(Queue fanoutQueue1, FanoutExchange fanoutExchange) {
        return BindingBuilder.bind(fanoutQueue1).to(fanoutExchange);
    }

    @Bean
    public Binding fanoutBinding2(Queue fanoutQueue2, FanoutExchange fanoutExchange) {
        return BindingBuilder.bind(fanoutQueue2).to(fanoutExchange);
    }
}

3.3.2 消息发送服务

创建一个生产者服务,使用 RabbitTemplate 发送消息。RabbitTemplate 是 Spring AMQP 提供的核心组件,用于发送和接收消息。

java 复制代码
package com.example.rabbitmqspringbootdemo.producer;

import com.example.rabbitmqspringbootdemo.config.RabbitMQConfig;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

@Service
public class MessageProducer {

    @Autowired
    private RabbitTemplate rabbitTemplate;

    public void sendDirectMessage(String message) {
        rabbitTemplate.convertAndSend(RabbitMQConfig.DIRECT_EXCHANGE, RabbitMQConfig.DIRECT_ROUTING_KEY, message);
        System.out.println("Direct Message Sent: " + message);
    }

    public void sendTopicMessageA(String message) {
        rabbitTemplate.convertAndSend(RabbitMQConfig.TOPIC_EXCHANGE, "topic.order.a", message);
        System.out.println("Topic Message A Sent: " + message);
    }

    public void sendTopicMessageB(String message) {
        rabbitTemplate.convertAndSend(RabbitMQConfig.TOPIC_EXCHANGE, "topic.user.b.register", message);
        System.out.println("Topic Message B Sent: " + message);
    }

    public void sendFanoutMessage(String message) {
        rabbitTemplate.convertAndSend(RabbitMQConfig.FANOUT_EXCHANGE, "", message); // Fanout Exchange 忽略 routing key
        System.out.println("Fanout Message Sent: " + message);
    }
}

3.3.3 测试生产者

您可以通过一个简单的 Controller 来触发消息发送:

java 复制代码
package com.example.rabbitmqspringbootdemo.controller;

import com.example.rabbitmqspringbootdemo.producer.MessageProducer;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class TestController {

    @Autowired
    private MessageProducer messageProducer;

    @GetMapping("/sendDirect")
    public String sendDirectMessage(@RequestParam String msg) {
        messageProducer.sendDirectMessage(msg);
        return "Direct message sent: " + msg;
    }

    @GetMapping("/sendTopicA")
    public String sendTopicMessageA(@RequestParam String msg) {
        messageProducer.sendTopicMessageA(msg);
        return "Topic message A sent: " + msg;
    }

    @GetMapping("/sendTopicB")
    public String sendTopicMessageB(@RequestParam String msg) {
        messageProducer.sendTopicMessageB(msg);
        return "Topic message B sent: " + msg;
    }

    @GetMapping("/sendFanout")
    public String sendFanoutMessage(@RequestParam String msg) {
        messageProducer.sendFanoutMessage(msg);
        return "Fanout message sent: " + msg;
    }
}

3.4 消费者实现

消费者通过 @RabbitListener 注解来监听指定队列的消息。我们将演示如何接收不同队列的消息,并进行手动确认。

java 复制代码
package com.example.rabbitmqspringbootdemo.consumer;

import com.example.rabbitmqspringbootdemo.config.RabbitMQConfig;
import com.rabbitmq.client.Channel;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Component;

import java.io.IOException;

@Component
public class MessageConsumer {

    @RabbitListener(queues = RabbitMQConfig.DIRECT_QUEUE)
    public void receiveDirectMessage(String message, Channel channel, Message msg) throws IOException {
        try {
            System.out.println("Received Direct Message: " + message);
            // 模拟业务处理
            // int i = 1/0; // 模拟异常
            channel.basicAck(msg.getMessageProperties().getDeliveryTag(), false);
            System.out.println("Direct Message Acknowledged.");
        } catch (Exception e) {
            System.err.println("Error processing Direct Message: " + e.getMessage());
            // 拒绝消息,并重新入队
            channel.basicNack(msg.getMessageProperties().getDeliveryTag(), false, true);
            System.err.println("Direct Message Nacked and Requeued.");
        }
    }

    @RabbitListener(queues = RabbitMQConfig.TOPIC_QUEUE_A)
    public void receiveTopicMessageA(String message, Channel channel, Message msg) throws IOException {
        try {
            System.out.println("Received Topic Message A: " + message);
            channel.basicAck(msg.getMessageProperties().getDeliveryTag(), false);
            System.out.println("Topic Message Acknowledged.");
        } catch (Exception e) {
            System.err.println("Error processing Topic Message A: " + e.getMessage());
            channel.basicNack(msg.getMessageProperties().getDeliveryTag(), false, true);
            System.err.println("Topic Message Nacked and Requeued.");
        }
    }

    @RabbitListener(queues = RabbitMQConfig.TOPIC_QUEUE_B)
    public void receiveTopicMessageB(String message, Channel channel, Message msg) throws IOException {
        try {
            System.out.println("Received Topic Message B: " + message);
            channel.basicAck(msg.getMessageProperties().getDeliveryTag(), false);
            System.out.println("Topic Message B Acknowledged.");
        } catch (Exception e) {
            System.err.println("Error processing Topic Message B: " + e.getMessage());
            channel.basicNack(msg.getMessageProperties().getDeliveryTag(), false, true);
            System.err.println("Topic Message Nacked and Requeued.");
        }
    }

    @RabbitListener(queues = RabbitMQConfig.FANOUT_QUEUE_1)
    public void receiveFanoutMessage1(String message, Channel channel, Message msg) throws IOException {
        try {
            System.out.println("Received Fanout Message 1: " + message);
            channel.basicAck(msg.getMessageProperties().getDeliveryTag(), false);
            System.out.println("Fanout Message 1 Acknowledged.");
        } catch (Exception e) {
            System.err.println("Error processing Fanout Message 1: " + e.getMessage());
            channel.basicNack(msg.getMessageProperties().getDeliveryTag(), false, true);
            System.err.println("Fanout Message 1 Nacked and Requeued.");
        }
    }

    @RabbitListener(queues = RabbitMQConfig.FANOUT_QUEUE_2)
    public void receiveFanoutMessage2(String message, Channel channel, Message msg) throws IOException {
        try {
            System.out.println("Received Fanout Message 2: " + message);
            channel.basicAck(msg.getMessageProperties().getDeliveryTag(), false);
            System.out.println("Fanout Message 2 Acknowledged.");
        } catch (Exception e) {
            System.err.println("Error processing Fanout Message 2: " + e.getMessage());
            channel.basicNack(msg.getMessageProperties().getDeliveryTag(), false, true);
            System.err.println("Fanout Message 2 Nacked and Requeued.");
        }
    }
}

3.5 消息确认机制 (ACK)

在上述消费者代码中,我们通过 Channel 对象实现了消息的手动确认。这是确保消息可靠性的关键步骤。

  • channel.basicAck(deliveryTag, multiple):用于肯定确认消息。deliveryTag 是消息的唯一标识,multiple 参数表示是否批量确认。设置为 false 表示只确认当前消息。
  • channel.basicNack(deliveryTag, multiple, requeue):用于否定确认消息。requeue 参数表示是否将消息重新放回队列。设置为 true 表示重新入队,false 表示丢弃或发送到死信队列(如果配置了)。
  • channel.basicReject(deliveryTag, requeue):与 basicNack 类似,但不支持批量操作。

通过手动确认机制,我们可以根据业务处理结果灵活地控制消息的生命周期。例如,在业务处理成功后进行 basicAck,在处理失败时进行 basicNack 并选择是否重新入队,从而避免消息丢失或重复消费的问题。

4. 总结

RabbitMQ 作为一款成熟稳定的消息中间件,在分布式系统、微服务架构中扮演着举足轻重的角色。它不仅能够帮助我们实现系统解耦、异步通信,还能有效应对高并发、削峰填谷等挑战,提升系统的整体性能和稳定性。

在实际项目中,除了本文介绍的基本概念和工作模式,RabbitMQ 还提供了许多高级特性,例如死信队列(Dead Letter Exchange)、延迟队列、消息优先级等,这些特性可以帮助我们构建更加健壮和灵活的消息系统。

相关推荐
胚芽鞘68141 分钟前
关于java项目中maven的理解
java·数据库·maven
岁忧2 小时前
(LeetCode 面试经典 150 题 ) 11. 盛最多水的容器 (贪心+双指针)
java·c++·算法·leetcode·面试·go
CJi0NG2 小时前
【自用】JavaSE--算法、正则表达式、异常
java
Hellyc2 小时前
用户查询优惠券之缓存击穿
java·redis·缓存
今天又在摸鱼3 小时前
Maven
java·maven
老马啸西风3 小时前
maven 发布到中央仓库常用脚本-02
java·maven
代码的余温3 小时前
MyBatis集成Logback日志全攻略
java·tomcat·mybatis·logback
ladymorgana3 小时前
【spring boot】三种日志系统对比:ELK、Loki+Grafana、Docker API
spring boot·elk·grafana
一只叫煤球的猫4 小时前
【🤣离谱整活】我写了一篇程序员掉进 Java 异世界的短篇小说
java·后端·程序员
斐波娜娜4 小时前
Maven详解
java·开发语言·maven