RabbitMQ 的工作模式

目录

工作模式

Simple(简单模式)

Work Queue(工作队列)

Publish/Subscribe(发布/订阅)

Exchange(交换机)?

Routing(路由模式)

Topics(通配符模式)

RPC(RPC通信)

Publisher Confirms(发布确认)

代码实现

Simple(简单模式)

生产者代码

消费者代码

Work Queues(工作队列)

生产者代码

消费者代码

Publish/Subscribe(发布/订阅)

生产者代码

消费者代码

Routing(路由模式)

生产者代码

消费者代码

Topics(通配符模式)

生产者代码

消费者代码

RPC(RPC通信)

客户端代码

服务端代码

Publisher Confirms(发布确认)

Publishing Messages Individually(单独确认)

Publishing Messages in Batches(批量确认)

Handling Publisher Confirms Asynchronously(异步确认)


RabbitMQ 共提供了 7 种工作模式进行消息传递:

在本篇文章中,我们就来学习 RabbitMQ 的工作模式,我们首先来了解这 7 种工作模式分别是怎样的

工作模式

Simple(简单模式)

P 表示生产者,是消息的发送方

C 表示消费者,是消息的接收者

Queue:表示消息队列,用于缓存消息,生产者生产的消息发送到队列中,消费者从队列中取出消息

简单模式下,只有一个生产者和一个消费者,生产者生产的消息存储到队列中后,都由这个消费者消费

特点:一个生产者 P,一个消费者 C,消息只能被消费一次,也称为 **点对点(Point-to-Point)**模式

适用场景:消息只能被单个消费者处理

在 RabbitMQ 入门中的入门代码的工作模式就是简单模式

Work Queue(工作队列)

此时有一个生产者和多个消费者

当生产者向队列中发送多条消息后,Work Queue 会将消息分配给不同的消费者,每个消费者接收到的消息不同,由多个消费者共同消费生产者生产的消息

例如:

由 A (生产者)发送不同消息,消息存储到 RabbitMQ 中,接着,由 B(消费者1) 和 C(消费者2) 共同消息A 发送的消息,此时,RabbitMQ 选择将第一条消息分配给 B,B 消费第一条消息,RabbitMQ 将第二条消息分配给 C,C 消费第二条消息…

B 和 C 接收到的消息是不同的,这两个消费者共同消费 A 发送的所有消息

特点:消息不会重复,分配给不同的消费者

适用场景:集群环境中实现异步处理

Publish/Subscribe(发布/订阅)

其中,X 表示的是交换机,在 发布/订阅 模式中,多了 Exchange 角色,因此,我们先来学习交换机相关知识

Exchange(交换机)

Exchange(交换机)的作用是接收生产者发送的消息,并将消息按照一定的规则路由到一个或多个队列中

生产者的消息都会先发送到交换机,然后再由交换机将消息路由到队列中

在前面 简单模式工作队列模式 下,图中都没有出现交换机,但实际上,生产者生产的消息都是先发送到交换机,然后再路由到队列中的。在前两种模式下,直接使用 RabbitMQ 提供的内置交换机就可以实现,因此,并没有突出交换机的存在,但实际上生产者生产的消息不会直接投递到队列中

在 RabbitMQ 中,交换机有 4 种类型:FanoutDirectTopicHeaders,不同的类型有着不同的路由策略

AMQP 协议中还有两种类型,System自定义,在这里,我们并不重点关注

Fanout:广播,将消息交给所有绑定到交换机的队列(Publish / Subscribe 模式

Direct:定向,将消息交给符合指定 routing key 的队列(Routing 模式

Topic:通配符,将消息交给符合 routing patterm(路由模式)的队列(Topics 模式

Headers:Headers 类型的交换机通过消息头部的属性来路由消息,而不依赖路由键的匹配规则来路由消息。根据发送的消息内容中的 headers 属性进行匹配,headers 类型的交换机性能会较差,因此也不太实用,基本上也不会进行使用

Exchange(交换机)只负责转发消息,并不具备存储消息的能力,因此,若是没有任何队列与 Exchange 绑定,或是没有符合路由规则的队列,消息就会丢失

接下来,我们来看 RoutingKeyBindingKey

RoutingKey:路由键,当生产者将消息发送给交换机时,会指定一个字符串,用于告诉交换机如何处理这个消息

BindingKey:绑定,RabbitMQ 中通过 Binding(绑定)将交换机与队列关联起来,在绑定时会指定一个 Binding Key,这样 RabbitMQ 就知道如何正确地将消息路由到队列

即,绑定时,需要的路由键是 BindingKey;发送消息时,需要的路由键是 RoutingKey

例如:

使用 BindingKey1 将交换机与 队列1 进行绑定,使用 BindingKey2 将交换机与 队列2 进行绑定

若在发送消息时,若设置 Routing Key 设置为 BindingKey1,交换机就会将消息路由到 队列1

即,当消息的 RoutingKey 与队列绑定的 BindingKey 相匹配时,消息才会被路由到这个队列中

其实,BindingKey 也属于路由键的一种,即,在绑定时使用的路由键,有时,也会使用 RoutingKey 表示 BindingKey,即使用 RoutingKey 表示 BindingKey 和 RoutingKey,因此,我们需要根据其使用场景进行区分

在了解了相关概念之后,我们继续看 Publish/ Subscribe 模式

上述有一个生产者 P,多个消费者 C1、C2,X 表示交换机,交换机将消息复制多份每个消费者接收到相同的消息

也就是说,生产者发送一条消息,经过交换机转发到不同的队列,不同的消费者从不同的队列中取出消息进行消费

特点:不同的消费者接收到的消息是相同的

适用场景:消息需要被多个消费者同时接收,如:实时通信或广播消息

Publish/Subscribe(发布/订阅)模式 与Work Queue(工作队列)模式 最大的区别就是:发布/订阅 模式下,不同消费者接收到的消息是相同的;而 工作队列 模式下,不同消费者接收到的消息是不同的

Routing(路由模式)

路由模式可以看做是 发布订阅模式 的变种,其在发布订阅模式的基础上,增加了路由 key

发布订阅模式会无条件的将所有消息发送给所有消费者,而路由模式下,交换机会根据 RoutingKey 的规则,将数据筛选后发送给对应的消费者队列

也就是说,只有满足条件的队列才会收到消息

如上图所示,Q1 通过 a 与交换机进行绑定,Q2 通过 a、b 和 c 与交换机进行绑定

当 P (生产者)在发送消息时,若设置 Routing Key 设置为 a,则此时 Q1 和 Q2 的BindingKey 都与其相匹配,消息就会被路由到 Q1 和 Q2 中

而当 P 发送消息时,设置Routing Key 设置为 b,此时,只有 Q2 的 BindingKey 与其相匹配,消息也就只会被路由到 Q2 中

适用场景:需要根据特定规则分发消息

Topics(通配符模式)

通配符模式,则是 路由模式 的变种,在 RoutingKey 的基础上,增加了通配符的功能,使得匹配更加灵活

Topics 和 Routing 的基本原理相同,即:生产者将消息发送给交换机,交换机根据 RoutingKey 将消息转发给与 RoutingKey 匹配的队列

而不同的是,Routing 模式下,需要RoutingKey 和 BingingKey 完全匹配;而 Topics 模式下,则是通配符匹配

在 BindingKey 中,存在两种特殊的字符串,用于模糊匹配

* :表示能够匹配任意一个单词

#:表示能够匹配任意多个单词(可以为 0 个)

Q1 通过 *.a.* 与交换机进行绑定,Q2 通过 *.*.b 和 c.# 与交换机进行绑定

当 P (生产者)在发送消息时,若设置 Routing Key 设置为 work.a.b,则此时 Q1 和 Q2 的BindingKey 都能够与其相匹配,消息就会被路由到 Q1 和 Q2 中

而当 P 发送消息时,设置Routing Key 设置为 a.a.a,此时,只有 Q1的 BindingKey 与其相匹配,消息也就只会被路由到 Q1中

适用场景:需要灵活匹配和过滤消息的场景

RPC(RPC通信)

RPC 通信过程中,没有生产者和消费者,而是通过两个队列实现了一个可回调的过程

例如:

客户端(Client)发送请求消息到指定队列(rpc_queue),并在消息属性中设置 reply_to 字段,这个字段指定了一个回调队列(amq.gen-Xa2…),这个回调队列用于接收服务端的响应消息

服务器(Server)从队列 rpc_queue 中取出请求消息,处理请求后,将响应消息发送到 reply_to 指定的回调队列(amq.gen-Xa2…)

客户端(Client)在回调队列上等待响应消息,一旦接收到响应,客户端就会检查消息的 correlation_id 属性,确保其是所期望的响应

简而言之,客户端将请求消息发送到 队列Q1 中,服务器从 Q1 中取出请求消息进行处理,然后将响应消息发送到 队列Q2 中,客户端从 Q2 中读取响应消息

从而实现了客户端向服务器发送请求,服务器返回对应的响应的功能

Publisher Confirms(发布确认)

Publisher Confirms 模式是 RabbitMQ 提供的一种确保消息可靠发送到 RabbitMQ 服务器的机制,在这种模式下,生产者可以等待 RabbitMQ 服务器的确认,以确保消息已经被服务器接收并处理

其过程为:

(1)生产者将 Channel 设置为 confirm 模式(通过调用 channel.confirmSelect() 完成),发布的每一条消息都会获得一个唯一的 ID,生产者可以将这些序列号与消息关联起来,以便追踪消息的状态

(2)当消息被 RabbitMQ 服务器接收并处理后,服务器会异步地向生产者发送一个确认(ACK),其中包含消息的唯一 ID,表示消息已经送达

通过 Publisher Confirms 模式,生产者可以确保消息被 RabbitMQ 服务器成功接收,从而避免消息丢失

**适用场景:**对数据安全性要求较高,如金融交易,订单处理等

在基本了解了 RabbitMQ 的 7 种工作模式后,我们就来通过代码简单实现一下这 7 种工作模式

代码实现

Simple(简单模式)

简单模式下,只有一个生产者和一个消费者,生产者生产的消息存储到队列后,都由这个消费者消费

RabbitMQ入门-CSDN博客中的入门代码的工作模式就是简单模式,因此,在这里就不再进行过多解释了

首先引入依赖:

        <!-- https://mvnrepository.com/artifact/com.rabbitmq/amqp-client -->
        <dependency>
            <groupId>com.rabbitmq</groupId>
            <artifactId>amqp-client</artifactId>
            <version>5.20.0</version>
        </dependency>
生产者代码
public class Producer {
    public static void main(String[] args) throws IOException, TimeoutException {
        // 1. 创建连接工厂
        ConnectionFactory factory = new ConnectionFactory();
        // 2. 设置参数
        factory.setHost("49.232.238.62"); // ip 的默认值为 localhost
        factory.setPort(5672); // 默认值为 5672
        factory.setVirtualHost("test01"); // 虚拟主机,默认值为 /
        // 账号
        factory.setUsername("admin"); // 用户名,默认为 guest
        factory.setPassword("123456"); // 密码,默认为 guest
        // 3. 创建连接 Connection
        Connection connection = factory.newConnection(); // 需要处理异常,在此处直接抛出,并不进行处理
        // 4. 创建 Channel
        Channel channel = connection.createChannel();
        // 5. 声明队列
        channel.queueDeclare("simple.test", true, false, false, null);
        // 6. 通过 channel 发送消息到队列中
        String message = "test...";
        channel.basicPublish("", "simple.test", null, message.getBytes());
        System.out.println("消息:" + message + " 发送成功");
        // 7. 释放资源
        channel.close();
        connection.close();
    }
}
消费者代码
public class Consumer {
    public static void main(String[] args) throws IOException, TimeoutException {
        // 1. 创建连接工厂
        ConnectionFactory factory = new ConnectionFactory();
        // 2. 设置参数
        factory.setHost("49.232.238.62"); // ip 的默认值为 localhost
        factory.setPort(5672); // 默认值为 5672
        factory.setVirtualHost("test01"); // 虚拟主机,默认值为 /
        // 账号
        factory.setUsername("admin"); // 用户名,默认为 guest
        factory.setPassword("123456"); // 密码,默认为 guest
        // 3. 创建连接 Connection
        Connection connection = factory.newConnection(); // 需要处理异常,在此处直接抛出,并不进行处理
        // 4. 创建 Channel
        Channel channel = connection.createChannel();
        // 5. 声明队列
        channel.queueDeclare("simple.test", true, false, false, null);
        // 6. 消费消息
        DefaultConsumer consumer = new DefaultConsumer(channel) {
            // 回调方法,当接收到消息后,自动执行该方法
            @Override
            public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
                System.out.println("成功接收到消息: " + new String(body));
            }
        };
        channel.basicConsume("simple.test", true, consumer);
        // 7. 释放资源
        channel.close();
        connection.close();
    }
}

Work Queues(工作队列)

生产者代码

工作队列模式下,由一个生产者生产消息,多个消费者共同接收消息,消费者之间是竞争关系,每个消息只能被一个消费者接收

由于我们每次连接时都要使用 IP、端口号、虚拟主机名等,因此,我们可以将它们提取出来,放到 Constants 类中:

public class Constants {
    public static final String HOST = "49.232.238.62";
    public static final int PORT = 5672;
    public static final String VIRTUAL_HOST = "test01";
    public static final String USER_NAME = "admin";
    public static final String USER_PASSWORD = "123456";
}

声明 工作队列 模式下使用的队列:

    // 工作模式
    public static final String WORK_QUEUE = "work.queue";

接下来,我们就来实现生产者的代码:

工作队列模式与简单模式的区别在于工作模式下有多个消费者,因此生产者的消费代码与简单模式下差别不大,但在发送消息时,我们一次发送 20 条消息:

public class Producer {
    public static void main(String[] args) throws IOException, TimeoutException {
        // 1. 创建连接工厂
        ConnectionFactory factory = new ConnectionFactory();
        // 2. 设置参数
        factory.setHost(Constants.HOST); // ip 的默认值为 localhost
        factory.setPort(Constants.PORT); // 默认值为 5672
        factory.setVirtualHost(Constants.VIRTUAL_HOST); // 虚拟主机,默认值为 /
        // 账号
        fac
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值