掌握RabbitMQ:全面知识点汇总与实践指南

前言

RabbitMQ 是基于 AMQP 高级消息队列协议的消息队列技术。

特点:它通过发布/订阅模型,实现了服务间的高度解耦。因为消费者不需要确保提供者的存在。

作用:服务间异步通信;顺序消费;定时任务;请求削峰;

1、AMQP协议定义

AMQP(Advanced Message Queuing Protocol)高级消息队列协议,是一个高效的、跨平台的应用层协议

MQTT(Message Queuing Telemetry Transport)消息队列遥测传输

特性 AMQP MQTT
适用场景 大型企业级应用、金融交易、云服务 物联网、移动应用、智能家居
通信模式 生产者-消费者 发布-订阅
消息大小 较大,适合复杂的消息结构 小型,适合简单的消息
QoS 级别 支持,但不如 MQTT 精细 详细的 QoS 级别,特别是针对 IoT 场景
性能要求 对性能有一定要求,但更注重可靠性和安全性 极低的带宽消耗和资源占用
安全性 强调端到端的安全性 支持基本的安全特性,适用于资源受限环境

2、AMQP机制

1>AMQP生产者、消费者工作机制

AMQP高级消息队列协议,基于生产者消费者模式,消息基于交换器Exchange、队列Queue、绑定Binding进行路由。

  • 生产者发送消息到Broker消息代理服务
  • 交换器接收生产者发送的消息,根据预定义规则,分发给一个或多个队列
  • 队列存储消息,直到消费者取走消息
  • 消费者,读取队列中的消息

AMQP定义了严格的消息结构,使用了类型化数据表示法描述消息内容来兼容不同的系统。

类型化数据表示法(Typed Representation of Data)是指在计算机编程语言中,数据和其相关联的类型信息一起被表示的方法。

2>AMQP消息传递方式

特性 点对点模式 (P2P) 发布/订阅模式 (Pub/Sub)
消息传递方式 每条消息仅被一个消费者处理 一条消息可以被多个消费者同时接收
队列数量 单个队列 每个消费者有自己的队列
生产者行为 直接发送到队列 发送到交换器,由交换器负责路由
消费者行为 从同一队列中竞争消费 各自独立消费自己的队列中的消息
适用场景 任务分配、工作流管理 广播通知、日志记录、事件驱动架构
扩展性 受限于单个队列的吞吐量 可以通过增加更多的消费者来提高整体吞吐量
复杂度 较低,易于理解和实现 需要考虑交换器类型、路由规则等因素,稍微复杂
  • 1、点对点
    生产者将消息发送到一个特定的队列中,而消费者则从该队列中获取消息
    每个消息只会被一个消费者处理,即使有多个消费者监听同一个队列。

竞争消费:多个实例尝试处理同一个消息时,可能出现重复消费或消息未及时得到处理的情况。

(1)竞争消费问题

在k8s部署多实例场景下,虽然提升了系统的吞吐量,通过调度器实现了负载均衡,多个实例从一个队列中读取消息,但是并发场景客观存在竞争消费的情况,导致重复消费消息。

(2)解决建议

合理配置消息队列、业务方法幂等性设计、分布式锁控制、增加监控告警和自动恢复动作。

java 复制代码
// 生产者代码片段
Channel channel = connection.createChannel();
channel.queueDeclare("task_queue", true, false, false, null);
String message = "Task to be processed";
channel.basicPublish("", "task_queue", null, message.getBytes());

// 消费者代码片段
DeliverCallback deliverCallback = (consumerTag, delivery) -> {
    String message = new String(delivery.getBody(), "UTF-8");
    System.out.println(" [x] Received '" + message + "'");
    // 执行任务...
    channel.basicAck(delivery.getEnvelope().getDeliveryTag(), false);
};
channel.basicConsume("task_queue", false, deliverCallback, consumerTag -> {});
  • 2、发布订阅
    生产者将消息发送到一个交换器(Exchange),而不是直接发送到队列。交换器根据预定义的路由规则(Binding Key)将消息转发给一个或多个匹配的队列。每个队列可以有多个消费者订阅,所有订阅者都能收到相同的消息副本

(1)主题分区

为不同类型的时间,创建不同的主题或分区,来减少不必要的复制。实例只订阅感兴趣的主题,降低资源开销

(2)限流

避免过载,限制单位之间内消费的最大消息

java 复制代码
// 生产者代码片段
Channel channel = connection.createChannel();
channel.exchangeDeclare("logs_exchange", "fanout");
String message = "Info log message";
channel.basicPublish("logs_exchange", "", null, message.getBytes());

// 消费者代码片段
String queueName = channel.queueDeclare().getQueue();
channel.queueBind(queueName, "logs_exchange", "");
DeliverCallback deliverCallback = (consumerTag, delivery) -> {
    String message = new String(delivery.getBody(), "UTF-8");
    System.out.println(" [x] Received '" + message + "'");
    // 处理日志...
};
channel.basicConsume(queueName, true, deliverCallback, consumerTag -> {});

3、AMQP消息只被消费一次

  • 1、合理配置消息队列ACK机制
    大多数消息队列都提供了手动确认(ACK)的功能,允许消费者成功处理后,主动通知消息代理
java 复制代码
// 使用 RabbitMQ 的手动确认示例
channel.basicConsume(queueName, false, deliverCallback, consumerTag -> {});
// 处理完成后发送 ACK
channel.basicAck(envelope.getDeliveryTag(), false);
  • 2、合理配置消息队列预取数量
    防止一次性去除较多的未处理消息。
java 复制代码
// 设置预取计数
channel.basicQos(prefetchCount);
  • 3、消费者幂等性设计
    针对消息全局唯一的ID,入库,每次收到消息时先检查是否已入库
    确保同一条消息多次处理的结果是一致的,避免重复的消息执行两次结果不一致
    增加补偿机制,比如退款,退积分等概念的操作
  • 4、分布式锁
    借助Redis 的 Redlock 算法协调多个消费者实例之间的消息处理,只有获取到锁的消息可以处理,其他的放弃或等待。
  • 5、监控告警机制
    监控消息队列服务健康情况,针对可能重复消费的消息及时告警到服务负责人介入处理。
  • 6、事务性消息
    指的是消息和业务操作,一起成功或一起失败的机制。
    (1)本地事务+补偿机制
    (2)二阶段提交
    引入协调者和参与者的概念。
    客户端向协调者发起事务请求,协调者询问各参与者是否准备好。全部准备好,则发出提交事务命令,否则全部回滚。每个参与者返回ack结果,协调者汇总执行结果,释放占用的资源。
    (3)三阶段提交
    针对二阶段提交完善事务性消息机制。
    首先客户端向协调者发起事务请求,协调者询问各参与者是否准备好。全部准备好,则发出预执行事务命令。各参与者收到命令,执行事务,但不提交。并返回ack,等待最终命令。协调者收到全部准备好,则发出提交事务命令。

4、AMQP 消息顺序消费

  • 单实例独占队列,可以保证顺序消费,但是分布式高可用场景一般都是多实例部署,独占队列无法解决消息顺序消费的问题。
  • 为了保证顺序消费,通常建议针对预取消息数量Prefetch Count设置为1:channel.basicQos(1);
  • 可以使用分布式锁确保消息消费是同步操作,并发安全,在成功处理消息后,手动发送ack确认到消息代理。
  • 另外使用幂等性设计来避免重复消费。
  • 增加补偿机制来处理幂等性设计无法保证的场景,比如退款等操作
  • 增加监控告警到服务负责人。
  • 可以对消息根据业务类型或特定的前缀规则,将不同的消息分到不同的分区或队列中,每个队列和分区内部是遵循先进先出规则来保证顺序消费的。

5、AMQP消息可靠性

  • 事务支持
    允许一组操作作为一个整体提交或回滚。
  • ACK确认机制
    当消息成功投递后,接收方会向发送方发送 ACK 确认;如果发生错误,则发送 NACK 拒绝。
  • 持久化选项
    可以选择是否将消息保存到磁盘上,以防服务中断时丢失重要数据。

6、RabbitMQ配置ACK

1>rabbitmq.conf或rabbitmq.ini开启配置

xml 复制代码
# 启用自动恢复功能,确保在网络中断后能够自动重连
connection.cached = true
# 设置心跳检测间隔,防止长时间无通信导致连接断开
heartbeat = 60
# 启用 Publisher Confirms,允许生产者收到消息确认
publisher_confirms = on

2>消费者手动确认

声明队列,确保队列存在

设置预取计数,限制每次从队列中拉取的消息数量为 1,以避免过载

开启手动确认模式,通过 channel.basicConsume 方法中的第二个参数 false 来关闭自动确认,改为手动确认

发送 ACK 确认,在成功处理完消息后,调用 channel.basicAck 方法发送确认

java 复制代码
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;
import com.rabbitmq.client.DeliverCallback;

public class RabbitMQConsumer {
    private final static String QUEUE_NAME = "task_queue";

    public static void main(String[] args) throws Exception {
        ConnectionFactory factory = new ConnectionFactory();
        factory.setHost("localhost");
        try (Connection connection = factory.newConnection();
             Channel channel = connection.createChannel()) {

            // 声明队列,确保它存在
            channel.queueDeclare(QUEUE_NAME, true, false, false, null);
            // 设置预取计数为 1,确保每次只处理一条消息
            channel.basicQos(1);

            // 开启手动确认模式
            DeliverCallback deliverCallback = (consumerTag, delivery) -> {
                String message = new String(delivery.getBody(), "UTF-8");
                System.out.println(" [x] Received '" + message + "'");
                try {
                    // 模拟任务处理时间
                    Thread.sleep(1000);
                } catch (InterruptedException _ignored) {
                    Thread.currentThread().interrupt();
                } finally {
                    System.out.println(" [x] Done");
                    // 手动发送 ACK 确认
                    channel.basicAck(delivery.getEnvelope().getDeliveryTag(), false);
                }
            };

            // 开始消费消息
            channel.basicConsume(QUEUE_NAME, false, deliverCallback, consumerTag -> {});
        }
    }
}

3>配置 Publisher Confirms和Transaction

允许生产者在发送消息后等待消息代理的确认

java 复制代码
// 开启 Publisher Confirms 模式
channel.confirmSelect();
try {
    channel.basicPublish(exchangeName, routingKey, null, message.getBytes());
    if (!channel.waitForConfirmsOrDie(timeout)) {
        // 处理未确认的消息...
    }
} catch (Exception e) {
    // 处理异常情况...
}
java 复制代码
Channel channel = connection.createChannel();
// 开启 Publisher Confirms 模式
channel.confirmSelect();
// 发送消息并等待确认
try {
    channel.basicPublish(exchangeName, routingKey, null, message.getBytes());
    if (!channel.waitForConfirmsOrDie(timeout)) {
        // 处理未确认的消息...
    }
} catch (Exception e) {
    // 处理异常情况...
}

// 开启事务模式
channel.txSelect();
try {
    channel.basicPublish(exchangeName, routingKey, null, message.getBytes());
    channel.txCommit();
} catch (Exception e) {
    channel.txRollback();
}

7、RabbitMQ配置协议

1>rabbitmq.conf

RabbitMQ默认是AMQP 0-9-1协议。支持设置监听端口。

支持启用SSL认证提高安全性。

支持设置心跳保证客户端和服务端连接保持活跃。

xml 复制代码
# 设置 AMQP 0-9-1 的监听端口
listeners.tcp.default = 5672
# 确保 AMQP 插件已启用,AMQP 0-9-1 是默认启用的
enabled_plugins = [rabbitmq_amqp1_0]

# 启用 SSL/TLS 支持
ssl_options.cacertfile = /path/to/ca_certificate.pem
ssl_options.certfile   = /path/to/server_certificate.pem
ssl_options.keyfile    = /path/to/private_key.pem
ssl_options.verify     = verify_peer
ssl_options.fail_if_no_peer_cert = true
# 设置 SSL/TLS 监听端口
listeners.ssl.default = 5671

# 设置心跳间隔时间为 60 秒
heartbeat = 60

8、RabbitMQ消息持久化

java 复制代码
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;

public class PersistentExample {
    private final static String QUEUE_NAME = "persistent_queue";
    private final static String EXCHANGE_NAME = "persistent_exchange";

    public static void main(String[] args) throws Exception {
        ConnectionFactory factory = new ConnectionFactory();
        factory.setHost("localhost");
        try (Connection connection = factory.newConnection();
             Channel channel = connection.createChannel()) {
            // 创建持久化的交换器
            channel.exchangeDeclare(EXCHANGE_NAME, "direct", true);
            // 创建持久化的队列
            channel.queueDeclare(QUEUE_NAME, true, false, false, null);
            // 绑定队列到交换器
            channel.queueBind(QUEUE_NAME, EXCHANGE_NAME, "routing_key");

            // 发送持久化的消息
            String message = "Persistent message!";
            AMQP.BasicProperties props = new AMQP.BasicProperties.Builder()
                    .deliveryMode(2) // 2 表示持久化
                    .build();
            channel.basicPublish(EXCHANGE_NAME, "routing_key", props, message.getBytes());
            System.out.println(" [x] Sent '" + message + "'");
        }
    }
}
  • 1、持久化队列
    channel.queueDeclare("durable_queue", true, false, false, null);
  • 2、交换器持久化
    确保在 RabbitMQ 启动时已经预声明了所有必要的交换器和队列绑定,以避免消息丢失
    channel.exchangeDeclare("durable_exchange", "direct", true);
  • 3、消息持久化
    delivery_mode 参数:设置为 2 表示持久化消息;设置为 1(默认)则表示非持久化消息
    channel.basicPublish("exchange_name", "routing_key", new AMQP.BasicProperties.Builder().deliveryMode(2).build(), message.getBytes());

9、RabbitMQ自动重连

网络中断或其他异常情况下自动重新连接到 RabbitMQ 并恢复之前的连接状态

java 复制代码
ConnectionFactory factory = new ConnectionFactory();
factory.setAutomaticRecoveryEnabled(true);
factory.setNetworkRecoveryInterval(5000);

10、RabbitMQ组件

组件 名称 说明
Producer 生产者 负责生成并发送消息的应用程序。
Consumer 消费者 负责接收并处理消息的应用程序。
Message 消息 承载业务数据的基本单元,包含消息体(Body)、属性(Properties)等信息。
Exchange 交换机 用于接收来自生产者的消息,并根据路由规则将其分发到一个或多个队列中。
Queue 队列 存储待处理消息的地方,消费者从中拉取消息进行处理。
Binding 绑定 定义了交换机和队列之间的关系,包括路由键等参数。
Virtual Host 虚拟主机 类似于命名空间的概念,用于隔离不同的应用环境,每个虚拟主机都有自己独立的一套用户、权限、交换机、队列等资源。

11、RabbitMQ核心组件交换器和路由键

交换器(Exchange)和路由键(Routing Key)是消息传递系统的核心组件,它们共同决定了消息如何从生产者传递到正确的队列。

消息提供方生产消息,根据预定规则,路由至匹配的一个或多个队列。

消息创建时设定路由键,消息发布到交换器时,通过队列路由键,把队列绑定到交换器上。消息到达交换器后,RabbitMQ会将消息的路由键与队列的路由键进行匹配。

若队列至少有一个消费者订阅,消息将以轮询方式发给消费者。

交换器 说明 应用场景
Direct 精确匹配路由键 只有当路由键完全匹配时,消息才会被发送到对应的队列。适用于一对一的消息分发。
Topic 基于通配符模式匹配路由键 适用于灵活的消息过滤和多条件匹配。
Fanout 广播所有消息给所有绑定的队列 适用于需要将相同消息发送给多个消费者的场景。
Headers 根据消息头属性进行路由 适用于复杂的消息路由需求,例如根据多个字段组合来决定消息去向。

1>Direct Exchange 精准匹配路由键交换器

根据路由键完全匹配队列,如果找不到匹配的队列,则消息会被丢弃。

  • 生产者
java 复制代码
// 创建 Direct Exchange
channel.exchangeDeclare("direct_logs", "direct");

// 绑定队列到 Exchange,并指定 Binding Key
String queueName = channel.queueDeclare().getQueue();
channel.queueBind(queueName, "direct_logs", "info");
channel.queueBind(queueName, "direct_logs", "warning");
channel.queueBind(queueName, "direct_logs", "error");

// 发送消息时指定 Routing Key
channel.basicPublish("direct_logs", "info", null, "Info log message".getBytes());
  • 消费者
java 复制代码
import com.rabbitmq.client.*;

public class DirectConsumer {
    private final static String EXCHANGE_NAME = "direct_logs";

    public static void main(String[] argv) throws Exception {
        ConnectionFactory factory = new ConnectionFactory();
        factory.setHost("localhost");
        try (Connection connection = factory.newConnection();
             Channel channel = connection.createChannel()) {

            // 声明 Exchange
            channel.exchangeDeclare(EXCHANGE_NAME, "direct");

            // 创建临时队列
            String queueName = channel.queueDeclare().getQueue();

            // 绑定队列到 Exchange,并指定 Binding Key
            if (argv.length < 1) {
                System.err.println("Usage: DirectConsumer [info] [warning] [error]");
                System.exit(1);
            }
            for (String severity : argv) {
                channel.queueBind(queueName, EXCHANGE_NAME, severity);
            }

            System.out.println(" [*] Waiting for messages. To exit press CTRL+C");

            DeliverCallback deliverCallback = (consumerTag, delivery) -> {
                String message = new String(delivery.getBody(), "UTF-8");
                System.out.println(" [x] Received '" + delivery.getEnvelope().getRoutingKey() + "':'" + message + "'");
            };
            channel.basicConsume(queueName, true, deliverCallback, consumerTag -> { });
        }
    }
}

2>Fanout Exchange广播交换器

广播所有消息给所有绑定的队列

  • 生产者
java 复制代码
// 创建 Fanout Exchange
channel.exchangeDeclare("logs", "fanout");

// 绑定队列到 Exchange
String queueName = channel.queueDeclare().getQueue();
channel.queueBind(queueName, "logs", "");

// 发送消息时不指定 Routing Key
channel.basicPublish("logs", "", null, "Broadcast log message".getBytes());
  • 消费者
java 复制代码
import com.rabbitmq.client.*;

public class FanoutConsumer {
    private final static String EXCHANGE_NAME = "logs";

    public static void main(String[] argv) throws Exception {
        ConnectionFactory factory = new ConnectionFactory();
        factory.setHost("localhost");
        try (Connection connection = factory.newConnection();
             Channel channel = connection.createChannel()) {

            // 声明 Exchange
            channel.exchangeDeclare(EXCHANGE_NAME, "fanout");

            // 创建临时队列
            String queueName = channel.queueDeclare().getQueue();

            // 绑定队列到 Exchange
            channel.queueBind(queueName, EXCHANGE_NAME, "");

            System.out.println(" [*] Waiting for messages. To exit press CTRL+C");

            DeliverCallback deliverCallback = (consumerTag, delivery) -> {
                String message = new String(delivery.getBody(), "UTF-8");
                System.out.println(" [x] Received '" + message + "'");
            };
            channel.basicConsume(queueName, true, deliverCallback, consumerTag -> { });
        }
    }
}

3>Topic Exchange 通配符路由器
*:匹配一个单词;#:匹配零个或多个单词

  • 生产者
java 复制代码
// 创建 Topic Exchange
channel.exchangeDeclare("topic_logs", "topic");

// 绑定队列到 Exchange,并指定 Binding Key 模式
String queueName = channel.queueDeclare().getQueue();
channel.queueBind(queueName, "topic_logs", "*.orange.*");
channel.queueBind(queueName, "topic_logs", "*.*.rabbit");
channel.queueBind(queueName, "topic_logs", "lazy.#");

// 发送消息时指定符合模式的 Routing Key
channel.basicPublish("topic_logs", "quick.orange.rabbit", null, "Quick orange rabbit".getBytes());
  • 消费者
java 复制代码
import com.rabbitmq.client.*;

public class TopicConsumer {
    private final static String EXCHANGE_NAME = "topic_logs";

    public static void main(String[] argv) throws Exception {
        ConnectionFactory factory = new ConnectionFactory();
        factory.setHost("localhost");
        try (Connection connection = factory.newConnection();
             Channel channel = connection.createChannel()) {

            // 声明 Exchange
            channel.exchangeDeclare(EXCHANGE_NAME, "topic");

            // 创建临时队列
            String queueName = channel.queueDeclare().getQueue();

            // 绑定队列到 Exchange,并指定 Binding Key 模式
            if (argv.length < 1) {
                System.err.println("Usage: TopicConsumer [binding_key_pattern]");
                System.exit(1);
            }
            for (String bindingKey : argv) {
                channel.queueBind(queueName, EXCHANGE_NAME, bindingKey);
            }

            System.out.println(" [*] Waiting for messages. To exit press CTRL+C");

            DeliverCallback deliverCallback = (consumerTag, delivery) -> {
                String message = new String(delivery.getBody(), "UTF-8");
                System.out.println(" [x] Received '" + delivery.getEnvelope().getRoutingKey() + "':'" + message + "'");
            };
            channel.basicConsume(queueName, true, deliverCallback, consumerTag -> { });
        }
    }
}

4>Headers Exchange 根据消息头属性进行路由

不依赖路由键,当消息的 headers 完全匹配时,才会将消息发送到对应的队列。

  • 生产者
java 复制代码
// 创建 Headers Exchange
channel.exchangeDeclare("headers_exchange", "headers");

// 绑定队列到 Exchange,并指定 Headers 匹配规则
Map<String, Object> headers = new HashMap<>();
headers.put("user_id", "12345");
headers.put("order_status", "pending");
AMQP.Queue.BindOk bindOk = channel.queueBind(queueName, "headers_exchange", "", headers);

// 发送带有 Headers 的消息
AMQP.BasicProperties props = new AMQP.BasicProperties.Builder()
        .headers(headers)
        .build();
channel.basicPublish("headers_exchange", "", props, "Message with specific headers".getBytes());
  • 消费者
java 复制代码
import com.rabbitmq.client.*;

public class HeadersConsumer {
    private final static String EXCHANGE_NAME = "headers_exchange";

    public static void main(String[] argv) throws Exception {
        ConnectionFactory factory = new ConnectionFactory();
        factory.setHost("localhost");
        try (Connection connection = factory.newConnection();
             Channel channel = connection.createChannel()) {

            // 声明 Exchange
            channel.exchangeDeclare(EXCHANGE_NAME, "headers");

            // 创建临时队列
            String queueName = channel.queueDeclare().getQueue();

            // 绑定队列到 Exchange,并指定 Headers 匹配规则
            Map<String, Object> headers = new HashMap<>();
            headers.put("user_id", "12345");
            headers.put("order_status", "pending");
            AMQP.Queue.BindOk bindOk = channel.queueBind(queueName, EXCHANGE_NAME, "", headers);

            System.out.println(" [*] Waiting for messages. To exit press CTRL+C");

            DeliverCallback deliverCallback = (consumerTag, delivery) -> {
                String message = new String(delivery.getBody(), "UTF-8");
                System.out.println(" [x] Received '" + message + "'");
            };
            channel.basicConsume(queueName, true, deliverCallback, consumerTag -> { });
        }
    }
}

12、RabbitMQ核心方法及参数说明

1>newConnection 创建连接工程并开启连接

java 复制代码
ConnectionFactory factory = new ConnectionFactory();
factory.setHost("localhost");
factory.setPort(5672);
factory.setUsername("guest");
factory.setPassword("guest");
Connection connection = factory.newConnection();

2>createChannel 创建信道

RabbitMQ 使用信道的方式来传输数据

信道是建立在真实的 TCP 连接内的虚拟连接,且每条 TCP 连接可以创建多个信道,每个信道都是独立的通信线路,可以并发地发送和接收消息。

java 复制代码
Channel channel = connection.createChannel();

3>exchangeDeclare 交换器声明

java 复制代码
channel.exchangeDeclare("my_exchange", "direct", true, false, null);

exchange: 交换器名称。

type: 交换器类型(如 "direct", "fanout", "topic", "headers")。

durable: 持久化标志,true 表示持久化,false 表示非持久化。

autoDelete: 自动删除标志,true 表示当最后一个队列断开时自动删除交换器。

internal: 内部交换器标志,true 表示该交换器只能被其他交换器使用,不能直接由生产者发布消息。

arguments: 其他可选参数,例如死信交换器、过期时间等。

4>queueDeclare 队列声明

java 复制代码
// 创建临时队列
String queueName = channel.queueDeclare().getQueue();

queue: 队列名称,为空字符串时表示创建临时队列。

durable: 持久化标志,true 表示持久化,false 表示非持久化。

exclusive: 排他性标志,true 表示仅当前连接可用,连接关闭后自动删除。

autoDelete: 自动删除标志,true 表示当最后一个消费者断开时自动删除队列。

arguments: 其他可选参数,例如死信队列、过期时间等。

5>queueBind 队列绑定

将队列绑定到指定的交换器上,并提供路由键或匹配规则

java 复制代码
channel.queueBind(queueName, "my_exchange", "routing_key");

queue: 队列名称。

exchange: 交换器名称。

routingKey: 路由键,对于某些类型的交换器(如 Direct 或 Topic),这个值是必须的;对于 Fanout 类型,通常为空字符串。

arguments: 可选参数,主要用于 Headers Exchange 的匹配规则

6>basicPublish 发布消息

向指定的交换器发布一条消息

java 复制代码
AMQP.BasicProperties props = new AMQP.BasicProperties.Builder()
        .contentType("text/plain")
        .deliveryMode(2) // 2 表示持久化
        .build();
channel.basicPublish("my_exchange", "routing_key", props, message.getBytes());

exchange: 交换器名称。

routingKey: 路由键。

props: 消息属性,包括内容类型、编码、持久化模式等。

body: 消息体,即要发送的数据。

7>basicConsume 消费消息

费来自指定队列的消息

java 复制代码
DeliverCallback deliverCallback = (consumerTag, delivery) -> {
    String message = new String(delivery.getBody(), "UTF-8");
    System.out.println(" [x] Received '" + message + "'");
};
channel.basicConsume(queueName, true, deliverCallback, consumerTag -> { });

queue: 队列名称。

autoAck: 自动确认标志,true 表示收到消息后立即确认,false 表示手动确认。

deliverCallback: 回调函数,用于处理接收到的消息。

cancelCallback: 取消回调函数,当消费者的取消通知到达时调用

8>basicAck 消息确认

手动确认模式下,当消费者成功处理完消息后,需要调用此方法来确认消息已被消费

java 复制代码
channel.basicAck(envelope.getDeliveryTag(), false);

9>basicNack 消息丢弃

当消费者无法处理某条消息时,可以拒绝这条消息,并决定是否重新入队或者丢弃

java 复制代码
// 第三个参数表示是否重新入队
channel.basicNack(envelope.getDeliveryTag(), false, true);

13、RabbitMQ镜像集群模式

搭建RabbitMQ保证消息队列的高可用。

创建的 queue,无论元数据还是 queue 里的消息都会存在于多个实例上,写消息到 queue 的时候,都会自动把消息同步到多个实例的 queue。

优点:高可用,单个节点挂掉,其他节点仍可用

缺点:高负载,如果某个队列消息很重,则镜像复制的实例下也会很重,性能开销大。

参考博客:消息队列中点对点与发布订阅区别

Powered by niaonao

相关推荐
NHuan^_^1 小时前
RabbitMQ基础篇之Java客户端 Work Queues
java·rabbitmq·java-rabbitmq
C182981825751 小时前
Rabbitmq Fanout如何保证不重复消费及应用场景
java·rabbitmq·java-rabbitmq
hao_wujing4 小时前
分布式 L2 网关下的 OVS 未知单播泛洪
分布式·wpf
杰克逊的日记4 小时前
SparkStreaming集群调优
大数据·分布式·spark
隔着天花板看星星4 小时前
Spark-Streaming有状态计算
大数据·分布式·spark
zhangliang6054 小时前
Spark创建多种数据格式的DataFrame
大数据·分布式·spark
pipape5 小时前
# Kafka组件化及拓展
分布式·kafka
2401_850410835 小时前
ELK+filebeat+kafka
分布式·elk·kafka
小汤猿人类5 小时前
RabbitMQ通过代码创建交换机和队列
分布式·rabbitmq
cleble6 小时前
(转)rabbitmq怎么保证消息不丢失?
rabbitmq