java中rabbitmq的介绍和使用

53 阅读17分钟

什么是rabbitmq

RabbitMQ是一个开源的消息队列系统,它实现了高级消息队列协议(AMQP)。AMQP是一种网络协议,它支持符合要求的客户端应用程序之间的消息传递。RabbitMQ是用Erlang编写的,它是一种适用于并发系统的编程语言,这使得RabbitMQ能够在多种平台上提供高可靠性和高效性能。

核心概念

  • 消息(Messages):在RabbitMQ中,消息是传输的单位,它由一些可用的属性和消息体组成。
  • 生产者(Producers):发送消息的应用程序通常称为生产者。
  • 队列(Queues):用于存储消息的数据结构。消息在被消费之前会一直存储在队列中。
  • 交换器(Exchanges):生产者将消息发送到交换器,而不是直接发送到队列。交换器负责根据路由键(routing key)将消息路由到一个或多个队列中。
  • 绑定(Bindings):绑定是交换器和队列之间的关系,它定义了交换器如何将消息路由到特定的队列。
  • 虚拟主机(Virtual Hosts):RabbitMQ允许你创建多个虚拟主机,每个虚拟主机都有自己的交换器、队列和绑定。这允许在单个RabbitMQ服务器上隔离不同的应用程序和环境。
  • 消费者(Consumers):从队列中接收消息的应用程序通常称为消费者。

特点和优势

  • 可靠性:RabbitMQ支持消息的持久化、事务和消息确认机制,确保消息不会丢失。
  • 灵活的路由:通过交换器和绑定,RabbitMQ提供了灵活的消息路由能力。
  • 高可用性:RabbitMQ支持集群模式,可以提供高可用性。
  • 扩展性:RabbitMQ可以根据需要轻松扩展,支持更多的生产者和消费者。
  • 多种客户端支持:RabbitMQ有多种编程语言的客户端库,如Python、Java、Ruby、PHP等。
  • 管理界面:RabbitMQ提供了一个易于使用的管理界面,可以用于管理和监控消息队列系统。

RabbitMQ广泛应用于各种场景,包括异步处理、应用解耦、负载均衡、分布式系统间的消息传递等。它的这些特性和优势使得RabbitMQ成为构建分布式、可靠消息传递系统的流行选择。

rabbitmq的安装 - mac

使用brew安装 brew update brew install rabbitmq

配置环境变量

在你的shell配置文件中添加如下代码,我的是os12系统,默认为zsh

终端根目录输入 vi .zshrc 编辑添加

注意:你的版本不一定是 3.13.1,通过输入brew list rabbitmq获取到对应的文件夹地址

 export RABBIT_HOME=/opt/homebrew/Cellar/rabbitmq/3.13.1
 export PATH=$PATH:$RABBIT_HOME/sbin

启用插件

启用rabbitmq management插件,用于开启浏览器后台页面

sudo rabbitmq-plugins enable rabbitmq_management

启动和停止

  • brew services list 查看 brew 管理的服务状态等
  • brew services start rabbitmq 启动
  • brew services stop rabbitmq 停止

配置用户

不配置用户远程连接是不被允许的,在开启rabbit后执行

命令行添加

# 添加用户
rabbitmqctl add_user admin password
# 设置为管理员
rabbitmqctl set_user_tags admin administrator

在管理页面添加(先执行下一步之后)

image2.png

访问可视化监控插件的界面

浏览器内输入 http://localhost:15672, 默认的用户名密码都是 guest

经过上方设置后 账号为 admin 密码为 password

为虚拟主机配置用户

image.png

向下滚动找到对应用户添加(这里是刚刚创建的用户)

image-1.png

一些命令

  • rabbitmqctl stop 停止
  • rabbitmq-plugins list 插件列表
  • rabbitmq-plugins enable management 启用插件
  • rabbitmq-plugins disable xxx 卸载插件

启动时可能的问题

通过日志查看 /opt/homebrew/var/log/rabbitmq/ 辨别问题

eacces

问题
error:{badmatch,{error,{{shutdown,{failed_to_start_child,auth,{"Error when reading /Users/xxxx/.erlang.cookie: eacces"

如果出现如上报错,这通常是由于权限问题(eacces 表示“权限被拒绝”)。

解决方式
  • 检查 Erlang cookie 文件的权限: 确保您的用户帐户有权访问和读取这个文件。您可以使用以下命令来检查和修改文件权限:
ls -l /Users/xxx/.erlang.cookie
chmod 600 /Users/xxx/.erlang.cookie

  • 检查文件所有者: 确保 Erlang cookie 文件属于启动 RabbitMQ 的用户。如果不是,您可以使用 chown 命令来更改文件所有者:
chown xxx /Users/xxx/.erlang.cookie

  • 确保 RabbitMQ 服务使用正确的用户运行。
  • 重启 RabbitMQ 服务: 在修改了权限之后,尝试重新启动 RabbitMQ 服务

java中的简单使用

发送

/**
 * 描述:     Hello World 的发送类,连接到RabbitMQ服务端,然后发送一条消息,然后退出。
 */
public class Send {

    private final static String QUEUE_NAME = "hello";

    public static void main(String[] args) throws IOException, TimeoutException {
        //创建连接工厂
        ConnectionFactory factory = new ConnectionFactory();
        //设置RabbitMQ地址
        factory.setHost("127.0.0.1");
        factory.setUsername("admin");
        factory.setPassword("password");
        //建立连接
        Connection connection = factory.newConnection();
        //获得信道
        Channel channel = connection.createChannel();
        /**
         * 声明一个队列。
         *
         * @param queue 队列名称。如果为空,服务器将自动生成一个唯一名称。
         * @param durable 是否持久化。如果为true,队列将在服务器重启后仍然存在。
         * @param exclusive 是否排他。如果为true,队列仅能被当前连接访问,
         *                  并且在连接关闭时会被删除。
         * @param autoDelete 是否自动删除。如果为true,当队列不再使用(没有消费者和没有消息)时,队列将被删除。
         * @param arguments 队列声明的其他可选参数。这些参数取决于服务器的配置,
         *                  例如消息的TTL(生存时间)、死信队列设置等。
         * @return 服务器返回的Queue.DeclareOk响应,其中包含队列的名称(当队列名称为空时尤为有用)。
         * @throws IOException 如果在声明队列时发生错误。
         */
        channel.queueDeclare(QUEUE_NAME, false, false, false, null);
        //发布消息
        String message = "Hello World 2!";
        /**
         * 发布消息到指定的交换机。
         *
         * @param exchange 交换机名称。不能为空,除非使用的是默认交换机。
         * @param routingKey 路由键。用于决定消息应该被投递到哪个队列,或者被哪些队列绑定接收。
         * @param props 消息的属性。可以设置消息的持久性、优先级、过期时间等。
         *              如果需要消息持久化,需要将DeliveryMode设置为2。
         * @param body 消息体。实际要发送的数据,通常是字节数组形式。
         * @throws IOException 如果在发送消息过程中发生错误。
         * @throws TimeoutException 如果消息发送超时。
         */
        channel.basicPublish("", QUEUE_NAME, null, message.getBytes("UTF-8"));
        System.out.println("发送了消息:" + message);
        //关闭连接
        channel.close();
        connection.close();
    }
}

接收


/**
 * 描述:     接收消息,并打印,持续运行
 */
public class Recv {
    private final static String QUEUE_NAME = "hello";

    public static void main(String[] args) throws IOException, TimeoutException {
        //创建连接工厂
        ConnectionFactory factory = new ConnectionFactory();
        //设置RabbitMQ地址
        factory.setHost("127.0.0.1");
        factory.setUsername("admin");
        factory.setPassword("password");
        //建立连接
        Connection connection = factory.newConnection();
        //获得信道
        Channel channel = connection.createChannel();
        //声明队列
        channel.queueDeclare(QUEUE_NAME, false, false, false, null);
        /**
         * 接收消息并消费
         * 启动一个消费者,并返回服务端生成的消费者标识
         * queue:队列名
         * autoAck:true 接收到传递过来的消息后acknowledged(应答服务器),false 接收到消息后不应答服务器
         * deliverCallback: 当一个消息发送过来后的回调接口
         * cancelCallback:当一个消费者取消订阅时的回调接口;取消消费者订阅队列时除了使用{@link Channel#basicCancel}之外的所有方式都会调用该回调方法
         * @return 服务端生成的消费者标识
         */
        channel.basicConsume(QUEUE_NAME, true, new DefaultConsumer(channel) {
            @Override
            public void handleDelivery(String consumerTag, Envelope envelope,
                                       BasicProperties properties, byte[] body) throws IOException {
                String message = new String(body, "UTF-8");
                System.out.println("收到消息:" + message);
            }
        });
    }
}

多个消费者的接收

发送

/**
 * 描述:     任务有所耗时,多个任务
 */
public class NewTask {

    private final static String TASK_QUEUE_NAME = "task_queue";

    public static void main(String[] args) throws IOException, TimeoutException {
        //创建连接工厂
        ConnectionFactory factory = new ConnectionFactory();
        //设置RabbitMQ地址
        factory.setHost("127.0.0.1");
        factory.setUsername("admin");
        factory.setPassword("password");
        //建立连接
        Connection connection = factory.newConnection();
        //获得信道
        Channel channel = connection.createChannel();
        //声明队列
        channel.queueDeclare(TASK_QUEUE_NAME, true, false, false, null);
        for (int i = 0; i < 10; i++) {
            String message;
            if (i % 2 == 0) {
                message = i + "...";
            } else {
                message = String.valueOf(i);
            }
            channel.basicPublish("", TASK_QUEUE_NAME, null, message.getBytes("UTF-8"));
            System.out.println("发送了消息:" + message);
        }

        channel.close();
        connection.close();
    }
}

接收

/**
 * 描述:     消费者,接收前面的批量消息
 */
public class Worker {

    private final static String TASK_QUEUE_NAME = "task_queue";
    //当启动多个的时候,如channel.basicConsume中的autoAck参数设置为自动消费(ture),
    //那么消息会被均匀分配,当3条消息被3个消费者接收,那么就是一人接一条。
    //此时并不是公平派遣(不在意任务的执行时间),只是按顺序分配给不同的消费者。
    public static void main(String[] args) throws IOException, TimeoutException {
        //创建连接工厂
        ConnectionFactory factory = new ConnectionFactory();
        //设置RabbitMQ地址
        factory.setHost("127.0.0.1");
        factory.setUsername("admin");
        factory.setPassword("password");
        //建立连接
        Connection connection = factory.newConnection();
        //获得信道
        final Channel channel = connection.createChannel();
        //声明队列
        channel.queueDeclare(TASK_QUEUE_NAME, true, false, false, null);
        System.out.println("开始接收消息");
        //公平派遣设置:希望处理的数量
        channel.basicQos(1);
        //公平派遣设置:第二个参数 非自动处理消息改为了false,注意下面一定要手动告诉消息已经处理完成
        channel.basicConsume(TASK_QUEUE_NAME, false, new DefaultConsumer(channel) {
            @Override
            public void handleDelivery(String consumerTag, Envelope envelope, BasicProperties properties, byte[] body) throws IOException {
                String message = new String(body, "UTF-8");
                System.out.println("收到了消息:" + message);
                try {
                    doWork(message);
                } finally {
                    System.out.println("消息处理完成");
                    //公平派遣设置:表示消息处理完成
                    //之后在有新消息时候,检查谁没完成,再把消息给完成了的消费者处理
                    channel.basicAck(envelope.getDeliveryTag(), false);
                }
            }
        });
    }

    private static void doWork(String task) {
        char[] chars = task.toCharArray();
        for (char ch : chars) {
            //NODE:有“.”的就等等处理,好区分。
            if (ch == '.') {
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

交换机模式&演示

RabbitMQ有四种交换机类型,分别是:Fanout exchange、Direct exchange、Topic exchange、Headers exchange

Fanout Exchange—扇出类型交换机

只需要简单的将队列绑定到该类型交换机上,该类型的交换机绑定队列时可以不指定路由键

当消息发送给该交换机后,它会将消息投递给与该交换机绑定的所有队列

很像广播,每台子网内的机器都会获得一份消息,Fanout交换机转发消息是最快的

发送

/**
 * EmitLog 类用于演示如何向 RabbitMQ 发送日志信息。
 * 主要功能是创建一个连接到 RabbitMQ 服务器的客户端,声明一个交换器,然后向该交换器发布消息。
 */
public class EmitLog {

    // 定义要使用的交换器名称
    private static final String EXCHANGE_NAME = "logs";

    /**
     * 程序的主入口函数。
     * @param args 命令行参数(未使用)
     * @throws IOException 如果发生 I/O 错误
     * @throws TimeoutException 如果连接或通道操作超时
     */
    public static void main(String[] args) throws IOException, TimeoutException {
        // 创建连接工厂并配置连接到 RabbitMQ 服务器的参数
        ConnectionFactory factory = new ConnectionFactory();
        factory.setHost("127.0.0.1");
        factory.setUsername("admin");
        factory.setPassword("password");

        // 建立与 RabbitMQ 服务器的连接
        Connection connection = factory.newConnection();

        // 创建一个新的通道
        Channel channel = connection.createChannel();

        // 声明一个交换器,使用内置的 FANOUT 类型,用于广播消息
        // 所有的消费者都可以收到,但是已经广播过的不会接收到。
        channel.exchangeDeclare(EXCHANGE_NAME, BuiltinExchangeType.FANOUT);

        // 准备要发送的消息内容
        String message = "info: Hello World!";

        // 向声明的交换器发布消息
        channel.basicPublish(EXCHANGE_NAME, "", null, message.getBytes("UTF-8"));
        System.out.println("发送了消息:" + message);

        // 关闭通道和连接,释放资源
        channel.close();
        connection.close();
    }
}

接收

/**
 * 这个类用于接收日志消息。
 * 它创建一个与RabbitMQ服务器的连接,并定义一个交换器来路由消息到队列中,
 * 然后从队列中消费消息。
 */
public class ReceiveLogs {
    private static final String EXCHANGE_NAME = "logs"; // 定义交换器的名称

    /**
     * 主函数:设置并连接到RabbitMQ服务器,声明交换器和队列,绑定队列到交换器,
     * 并开始接收和处理消息。
     *
     * @param args 命令行参数(未使用)
     * @throws IOException      如果发生I/O错误
     * @throws TimeoutException 如果连接超时
     */
    public static void main(String[] args) throws IOException, TimeoutException {
        // 创建连接工厂并设置RabbitMQ服务器的主机地址、用户名和密码
        ConnectionFactory factory = new ConnectionFactory();
        factory.setHost("127.0.0.1");
        factory.setUsername("admin");
        factory.setPassword("password");

        // 建立与RabbitMQ服务器的连接
        Connection connection = factory.newConnection();

        // 创建通道
        Channel channel = connection.createChannel();

        // 声明一个风扇型交换器
        channel.exchangeDeclare(EXCHANGE_NAME, BuiltinExchangeType.FANOUT);

        // 声明一个队列,并返回队列名称,非持久的,非自动删除的队列,可以多次启动
        String queueName = channel.queueDeclare().getQueue();

        // 将队列绑定到交换器上
        channel.queueBind(queueName, EXCHANGE_NAME, "");

        System.out.println("开始接收消息");

        // 定义一个消息消费者并消费消息
        Consumer consumer = new DefaultConsumer(channel) {
            /**
             * 当有消息到达时处理消息。
             *
             * @param consumerTag 消费者的标签
             * @param envelope 消息的环境信息,包括消息ID等
             * @param properties 消息的属性
             * @param body 消息体
             * @throws IOException 如果处理消息时发生I/O错误
             */
            @Override
            public void handleDelivery(String consumerTag, Envelope envelope,
                                       BasicProperties properties, byte[] body) throws IOException {
                // 将消息体从字节转换为字符串并打印出来
                String message = new String(body, "UTF-8");
                System.out.println("收到消息:" + message);
            }
        };

        // 开始消费队列中的消息
        channel.basicConsume(queueName, true, consumer);
    }
}


Direct exchange—直接类型交换机

要求消息带的路由键和绑定的路由键完全匹配,这是一个完整的匹配。

比如一个队列A绑定到该交换机上的路由键是“abc”,则只有指定的路由键是“abc”的消息才被投递给队列A,其他的不会投递给队列A

发送

/**
 * 直接路由日志的示例类。
 * 此类用于演示如何通过RabbitMQ发送不同级别的日志消息。
 */
public class EmitLogDirect {

    // 定义一个直接交换机的名称
    private static final String EXCHANGE_NAME = "direct_logs";

    /**
     * 主函数:创建连接到RabbitMQ服务器,声明交换机,并发布消息到交换机。
     *
     * @param args 命令行参数(未使用)
     * @throws IOException      如果发生I/O错误
     * @throws TimeoutException 如果连接超时
     */
    public static void main(String[] args) throws IOException, TimeoutException {
        // 创建连接工厂,用于生产连接到RabbitMQ服务器
        ConnectionFactory factory = new ConnectionFactory();
        // 设置连接工厂要连接的主机地址
        factory.setHost("127.0.0.1");
        factory.setUsername("admin");
        factory.setPassword("password");
        // 建立一个新的连接
        Connection connection = factory.newConnection();
        // 创建一个新的通道
        Channel channel = connection.createChannel();
        // 声明一个直接类型的交换机
        channel.exchangeDeclare(EXCHANGE_NAME, BuiltinExchangeType.DIRECT);

        // 准备发送的消息
        String message = "info:Hello World!";

        // 向交换机发送消息,消息路由键为"info"
        channel.basicPublish(EXCHANGE_NAME, "info", null, message.getBytes("UTF-8"));
        System.out.println("发送了消息," + "等级为info,消息内容:" + message);

        // 改变消息内容,然后发送到交换机,此次使用"warning"作为路由键
        message = "warning:Hello World!";
        channel.basicPublish(EXCHANGE_NAME, "warning", null, message.getBytes("UTF-8"));
        System.out.println("发送了消息," + "等级为warning,消息内容:" + message);

        // 再次改变消息内容,发送到交换机,使用"error"作为路由键
        message = "error:Hello World!";
        channel.basicPublish(EXCHANGE_NAME, "error", null, message.getBytes("UTF-8"));
        System.out.println("发送了消息," + "等级为error,消息内容:" + message);

        // 关闭通道和连接以释放资源
        channel.close();
        connection.close();
    }
}

接收不同等级的消息

/**
 * 描述:     接收3个等级的日志
 */
public class ReceiveLogsDirect1 {
    private static final String EXCHANGE_NAME = "direct_logs";

    public static void main(String[] args) throws IOException, TimeoutException {
        ConnectionFactory factory = new ConnectionFactory();
        factory.setHost("127.0.0.1");
        factory.setUsername("admin");
        factory.setPassword("password");
        Connection connection = factory.newConnection();
        Channel channel = connection.createChannel();
        channel.exchangeDeclare(EXCHANGE_NAME, BuiltinExchangeType.DIRECT);
        //生成一个随机的临时的queue
        String queueName = channel.queueDeclare().getQueue();
        //一个交换机同时绑定3个queue,接收其它消息类型更换绑定即可
        //同时开启一个新的接收者即可实现分开按类型接收
        channel.queueBind(queueName, EXCHANGE_NAME, "info");
        channel.queueBind(queueName, EXCHANGE_NAME, "warning");
        channel.queueBind(queueName, EXCHANGE_NAME, "error");

        System.out.println("开始接收消息");
        Consumer consumer = new DefaultConsumer(channel) {
            @Override
            public void handleDelivery(String consumerTag, Envelope envelope,
                                       BasicProperties properties, byte[] body) throws IOException {
                String message = new String(body, "UTF-8");
                System.out.println("收到消息:" + message);
            }
        };
        channel.basicConsume(queueName, true, consumer);
    }
}

Topic Exchange—主题类型交换机

将路由键和某模式进行匹配。此时队列需要绑定某一个模式上。符号#匹配0个或多个单词,符号 *匹配一个单词。

发送

/**
 * EmitLogTopic 类用于演示如何向 RabbitMQ 的主题交换器发送消息。
 * 主函数中创建了一个连接到 RabbitMQ 服务器的连接,并声明了一个主题交换器。
 * 然后,向该交换器发布了一系列消息,每个消息都绑定到不同的路由键上。
 */
public class EmitLogTopic {

    // 定义要声明的交换器的名称
    private static final String EXCHANGE_NAME = "topic_logs";

    /**
     * 主函数:创建连接和通道,声明交换器,发布消息到交换器。
     * @param args 命令行参数(未使用)
     * @throws IOException 如果发生 I/O 错误
     * @throws TimeoutException 如果连接超时
     */
    public static void main(String[] args) throws IOException, TimeoutException {
        // 创建连接工厂并设置主机地址为本地主机
        ConnectionFactory factory = new ConnectionFactory();
        factory.setHost("localhost");

        // 建立到 RabbitMQ 服务器的新连接
        Connection connection = factory.newConnection();

        // 创建一个新的通道
        Channel channel = connection.createChannel();

        // 声明一个主题类型的交换器
        channel.exchangeDeclare(EXCHANGE_NAME, BuiltinExchangeType.TOPIC);

        // 准备发送的消息内容
        String message = "Animal World";

        // 定义一系列路由键,以演示不同的绑定
        String[] routingKeys = new String[9];
        routingKeys[0] = "quick.orange.rabbit";
        routingKeys[1] = "lazy.orange.elephant";
        routingKeys[2] = "quick.orange.fox";
        routingKeys[3] = "lazy.brown.fox";
        routingKeys[4] = "lazy.pink.rabbit";
        routingKeys[5] = "quick.brown.fox";
        routingKeys[6] = "orange";
        routingKeys[7] = "quick.orange.male.rabbit";
        routingKeys[8] = "lazy.orange.male.rabbit";

        // 循环发布消息到交换器,每个消息绑定到不同的路由键
        for (int i = 0; i < routingKeys.length; i++) {
            channel.basicPublish(EXCHANGE_NAME, routingKeys[i], null, message.getBytes("UTF-8"));
            System.out.println("发送了:" + message + " routingKey:" + routingKeys[i]);
        }

        // 关闭通道和连接
        channel.close();
        connection.close();
    }
}

接收

/**
 * 这个类用于演示如何从一个RabbitMQ的主题交换器中接收消息。
 * 它会连接到本地的RabbitMQ服务器,声明一个主题交换器,创建一个临时队列,
 * 并将该队列绑定到一个带有通配符的主题上,从而可以接收匹配该主题的消息。
 */
public class ReceiveLogsTopic1 {

    private static final String EXCHANGE_NAME = "topic_logs"; // 定义交换器的名称

    /**
     * 主函数:创建连接、通道,声明交换器、队列,绑定队列和交换器,以及消费消息。
     * @param args 命令行参数(未使用)
     * @throws IOException 如果发生I/O错误
     * @throws TimeoutException 如果连接超时
     */
    public static void main(String[] args) throws IOException, TimeoutException {
        // 创建连接工厂并设置连接RabbitMQ服务器的参数
        ConnectionFactory factory = new ConnectionFactory();
        factory.setHost("127.0.0.1");
        factory.setUsername("admin");
        factory.setPassword("password");

        // 建立与RabbitMQ服务器的连接
        Connection connection = factory.newConnection();
        // 创建一个新的通道
        Channel channel = connection.createChannel();

        // 声明一个主题交换器
        channel.exchangeDeclare(EXCHANGE_NAME, BuiltinExchangeType.TOPIC);

        // 生成一个随机的临时的queue
        String queueName = channel.queueDeclare().getQueue();

        // 使用通配符绑定队列到交换器,以接收特定模式的消息
        // * 匹配一个字符,# 匹配多个字符
        String routingKey = "*.orange.*";
        channel.queueBind(queueName, EXCHANGE_NAME, routingKey);

        System.out.println("开始接收消息");

        // 定义消息消费者并启动消费过程
        Consumer consumer = new DefaultConsumer(channel) {
            @Override
            public void handleDelivery(String consumerTag, Envelope envelope,
                                       BasicProperties properties, byte[] body) throws IOException {
                // 消费消息并打印出来
                String message = new String(body, "UTF-8");
                System.out.println("收到消息:" + message + " routingKey: " + envelope.getRoutingKey());
            }
        };
        channel.basicConsume(queueName, true, consumer);
    }
}

Headers Exchanges

这种不常用,可以选择性忽略

整合spring

pom添加依赖


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

生产者

配置

/**
 * 描述:     rabbitmq配置类
 */
@Configuration // 标识为配置类
public class TopicRabbitConfig {

    /**
     * 创建并配置一个名为 "queue1" 的队列。
     *
     * @return Queue 返回一个RabbitMQ队列对象。
     */
    @Bean
    public Queue queue1() {
        return new Queue("queue1");
    }

    /**
     * 创建并配置一个名为 "queue2" 的队列。
     *
     * @return Queue 返回一个RabbitMQ队列对象。
     */
    @Bean
    public Queue queue2() {
        return new Queue("queue2");
    }

    /**
     * 创建并配置一个名为 "bootExchange" 的主题交换器。
     *
     * @return TopicExchange 返回一个RabbitMQ主题交换器对象。
     */
    @Bean
    TopicExchange exchange() {
        return new TopicExchange("bootExchange");
    }

    /**
     * 将队列 "queue1" 绑定到 "bootExchange" 交换器上,绑定键为 "dog.red"。
     *
     * @param queue1 队列对象。
     * @param exchange 交换器对象。
     * @return Binding 返回一个绑定对象。
     */
    @Bean
    Binding bingdingExchangeMessage1(Queue queue1, TopicExchange exchange) {
        return BindingBuilder.bind(queue1).to(exchange).with("dog.red");
    }

    /**
     * 将队列 "queue2" 绑定到 "bootExchange" 交换器上,绑定键为 "dog.#"。
     * 这样的绑定可以匹配以 "dog" 开头的所有路由键。
     *
     * @param queue2 队列对象。
     * @param exchange 交换器对象。
     * @return Binding 返回一个绑定对象。
     */
    @Bean
    Binding bingdingExchangeMessage2(Queue queue2, TopicExchange exchange) {
        return BindingBuilder.bind(queue2).to(exchange).with("dog.#");
    }
}

发送信息

/**
 * 描述:     发送消息
 */
@Component
public class MsgSender {

    @Autowired
    private AmqpTemplate rabbitmqTemplate;

    /**
     * 此方法用于发送一条消息到RabbitMQ,消息内容与路由键相关联。
     * @param message 消息内容,描述为"This is message 1, routing key is dog.red"
     * @see #rabbitmqTemplate RabbitMQTemplate实例,用于实际的消息发送操作
     */
    public void send1() {
        String message = "This is message 1, routing key is dog.red";
        System.out.println("发送了:"+message);
        // 使用RabbitMQTemplate,通过指定的交换机"bootExchange"和路由键"dog.red"发送消息
        this.rabbitmqTemplate.convertAndSend("bootExchange", "dog.red", message);
    }

    public void send2() {
        String message = "This is message 2, routing key is dog.black";
        System.out.println("发送了:"+message);
        this.rabbitmqTemplate.convertAndSend("bootExchange", "dog.black", message);
    }
}

消费者

/**
 * 描述:     消费者1
 */
@Component
@RabbitListener(queues = "queue1")
public class Receiver1 {

    @RabbitHandler
    public void process(String message) {
        System.out.println("Receiver1: " + message);
    }
}
/**
 * 描述:     消费者2
 */
@Component
@RabbitListener(queues = "queue2")
public class Receiver2 {

    @RabbitHandler
    public void process(String message) {
        System.out.println("Receiver2: " + message);
    }
}