JAVA高级工程师-消息中间件RabbitMQ工作模式(二)

总览

模型 交换机类型 适用场景 特点
简单队列 (默认) 简单点对点 最简单,无路由
工作队列 (默认) 任务分发、负载均衡 多个消费者竞争
发布/订阅 Fanout 广播通知、日志收集 一对多广播
路由 Direct 分类消息处理 精确路由匹配
主题 Topic 复杂事件路由 模式匹配,最灵活

1.简单模式

  • P:生产者,发送消息到消息队列

  • C:消费者:消息的接受者,会一直等待消息到来。

  • queue:消息队列,图中红色部分。类似一个邮箱,可以缓存消息;生产者向其中投递消息,消费者从其中取出消息。

特点:

  • 一对一通信

  • 无交换机参与

  • 自动使用默认的 Direct Exchange

java 复制代码
/**
 * 构建生产者,发送消息
 */
public class FooProducer {

    public static void main(String[] args) throws Exception {

        // 1. 创建连接工厂以及参数配置
        ConnectionFactory factory = new ConnectionFactory();
        factory.setHost("192.168.1.122");
        factory.setPort(5672);
        factory.setVirtualHost("/");
        factory.setUsername("imooc");
        factory.setPassword("imooc");

        // 2. 创建连接Connection
        Connection connection = factory.newConnection();

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

        // 4. 创建队列Queue(简单模式不需要交换机Exchange)
        /**
         * queue: 队列名
         * durable: 是否持久化,true:重启后,队列依然存在,否则不存在
         * exclusive: 是否独占,true:只能有一个消费者监听这个队列,一般设置为false
         * autoDelete: 是否自动删除,true:当没有消费者的时候,自动删除队列
         * arguments: map类型其他参数
         */
        channel.queueDeclare("hello", true, false, false, null);

        // 5. 向队列发送消息
        /**
         * exchange: 交换机名称,简单模式没有,直接设置为 ""
         * routingKey: 路由key,映射路径,如果交换机是默认"",则路由key和队列名一致
         * props: 配置信息
         * body: 消息数据
         */
        String msg = "Hello 慕课网~";
        channel.basicPublish("", "hello", null, msg.getBytes());

        // 7. 释放
        channel.close();
        connection.close();
    }
}

生产者基本过程:

  1. 创建连接工厂以及参数配置
  2. 创建连接Connection
  3. 创建管道Channel
  4. 创建队列Queue(简单模式不需要交换机Exchange)
  5. 向队列发送消息
  6. 释放
  • channel.close();
  • connection.close();
java 复制代码
/**
 * 构建消费者,监听消息
 */
public class FooConsumer {

    public static void main(String[] args) throws Exception {

        // 1. 创建连接工厂以及参数配置
        ConnectionFactory factory = new ConnectionFactory();
        factory.setHost("192.168.1.122");
        factory.setPort(5672);
        factory.setVirtualHost("/");
        factory.setUsername("imooc");
        factory.setPassword("imooc");

        // 2. 创建连接Connection
        Connection connection = factory.newConnection();

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

        // 4. 创建队列Queue(简单模式不需要交换机Exchange)
        /**
         * queue: 队列名
         * durable: 是否持久化,true:重启后,队列依然存在,否则不存在
         * exclusive: 是否独占,true:只能有一个消费者监听这个队列,一般设置为false
         * autoDelete: 是否自动删除,true:当没有消费者的时候,自动删除队列
         * arguments: map类型其他参数
         */
        channel.queueDeclare("hello", true, false, false, null);

        // 5. 监听并消费消息
        /**
         * queue: 监听的队列名
         * autoAck: 是否自动确认,true:告知mq消费者已经消费的确认通知
         * callback: 回调函数,处理监听的消息
         */
        Consumer consumer = new DefaultConsumer(channel) {
            /**
             * 重写消息配送方法
             * @param consumerTag: 消息标签(标识)
             * @param envelope: 信封(一些信息,比如交换机路由等信息)
             * @param properties: 配置信息,和生产者的一致
             * @param body: 消息数据
             * @throws IOException
             */
            @Override
            public void handleDelivery(String consumerTag,
                                       Envelope envelope,
                                       AMQP.BasicProperties properties,
                                       byte[] body) throws IOException {

                System.out.println("consumerTag = " + consumerTag);
                System.out.println("envelope = " + envelope.toString());
                System.out.println("properties = " + properties.toString());
                System.out.println("body = " + new String(body));

                super.handleDelivery(consumerTag, envelope, properties, body);
            }
        };
        channel.basicConsume("hello", true, consumer);

        // 不需要关闭连接,则持续监听

    }
}

消费者基本过程:

  1. 创建连接工厂以及参数配置
  2. 创建连接Connection
  3. 创建管道Channel
  4. 创建队列Queue(简单模式不需要交换机Exchange)
  5. 监听并消费消息
  6. 不需要关闭连接,则持续监听

目前用的是简单模式,不需要交换机的。

2.工作队列(Work Queue)

多个消费者监听同一队列,用于任务分发。消费者接收到消息后, 通过线程池异步消费。但是一个消息只能被一个消费者获取。work queue 常用于避免消息堆积问题。工作队列没有交换机。

  • P:生产者,发布任务。

  • C1:消费者 1,领取任务并且完成任务,假设完成速度较慢(模拟耗时)

  • C2:消费者 2,领取任务并且完成任务,假设完成速度较快

适用场景:

如果任务量很大很多,而一个消费者处理不过来,则此时可以使用工作队列,比如短信发送在一个系统里会有很多场景进行发送给用户,所以量很大的时候,也可以分配给多个消费者去进行消费发短信即可。这就像上班工作量很大,就需要招人共同完成一些任务是一个道理。

特点:

  • 多个消费者竞争消费,但是只能一个消费者消费

  • 支持消息确认机制

  • 实现负载均衡

生产者:

和简单模式的差不多,把发消息那段代码换成这个。

java 复制代码
package cc.wj.test.rabbitmq.learn.work;

import cc.wj.test.rabbitmq.learn.MqConst;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;

/**
 * 简单模式
 *
 * @author wj
 * @version 1.0
 * @date 2026/1/21 16:11
 */
public class WorkProducer {
    public static void main(String[] args) throws Exception {

        // 1. 创建连接工厂以及参数配置
        ConnectionFactory factory = new ConnectionFactory();
        factory.setHost("192.168.146.130");
        factory.setPort(5672);
        factory.setVirtualHost("/");
        factory.setUsername("guest");
        factory.setPassword("guest");

        // 2. 创建连接Connection
        Connection connection = factory.newConnection();

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

        // 4. 创建队列Queue(简单模式不需要交换机Exchange)
        /**
         * queue: 队列名
         * durable: 是否持久化,true:重启后,队列依然存在,否则不存在
         * exclusive: 是否独占,true:只能有一个消费者监听这个队列,一般设置为false
         * autoDelete: 是否自动删除,true:当没有消费者的时候,自动删除队列
         * arguments: map类型其他参数
         */
        channel.queueDeclare(MqConst.QUEUE_NAME_WORK, true, false, false, null);

        // 5. 向队列发送消息
        /**
         * exchange: 交换机名称,简单模式没有,直接设置为 ""
         * routingKey: 路由key,映射路径,如果交换机是默认"",则路由key和队列名一致
         * props: 配置信息
         * body: 消息数据
         */
        for (int i = 0; i < 20; i++) {
            String msg = "Hello Work~" + i;
            channel.basicPublish("", MqConst.QUEUE_NAME_WORK, null, msg.getBytes());
        }


        // 7. 释放
        channel.close();
        connection.close();
    }
}

消费者:

构建两个消费者:

只需要复制简单模式再修改队列名即可

java 复制代码
package cc.wj.test.rabbitmq.learn.work;

import cc.wj.test.rabbitmq.learn.MqConst;
import com.rabbitmq.client.*;

import java.io.IOException;

/**
 * @author wj
 * @version 1.0
 * @date 2026/1/21 16:13
 */
public class FooConsumer1 {
    public static void main(String[] args) throws Exception {

        // 1. 创建连接工厂以及参数配置
        ConnectionFactory factory = new ConnectionFactory();
        factory.setHost("192.168.146.130");
        factory.setPort(5672);
        factory.setVirtualHost("/");
        factory.setUsername("guest");
        factory.setPassword("guest");

        // 2. 创建连接Connection
        Connection connection = factory.newConnection();

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

        // 4. 创建队列Queue(简单模式不需要交换机Exchange)
        /**
         * queue: 队列名
         * durable: 是否持久化,true:重启后,队列依然存在,否则不存在
         * exclusive: 是否独占,true:只能有一个消费者监听这个队列,一般设置为false
         * autoDelete: 是否自动删除,true:当没有消费者的时候,自动删除队列
         * arguments: map类型其他参数
         */
        channel.queueDeclare(MqConst.QUEUE_NAME_WORK, true, false, false, null);

        // 5. 监听并消费消息
        /**
         * queue: 监听的队列名
         * autoAck: 是否自动确认,true:告知mq消费者已经消费的确认通知
         * callback: 回调函数,处理监听的消息
         */
        Consumer consumer = new DefaultConsumer(channel) {
            /**
             * 重写消息配送方法
             *
             * @param consumerTag: 消息标签(标识)
             * @param envelope:    信封(一些信息,比如交换机路由等信息)
             * @param properties:  配置信息,和生产者的一致
             * @param body:        消息数据
             * @throws IOException
             */
            @Override
            public void handleDelivery(String consumerTag,
                                       Envelope envelope,
                                       AMQP.BasicProperties properties,
                                       byte[] body) throws IOException {
                System.out.println("body = 消费者1  :" + new String(body));

                super.handleDelivery(consumerTag, envelope, properties, body);
            }
        };
        channel.basicConsume(MqConst.QUEUE_NAME_WORK, true, consumer);

        // 不需要关闭连接,则持续监听
    }
}

先启动消费者,在启动生成者,输出如下:

我们发现消费者是按照轮询消费的,但这种消费存在一个问题,假如 Consumer1 处理能力极快,Consumer2 (代码中休眠了 2s)处理能力极慢,这是 Consumer2 会严重拖累整体消费进度,而 Consuemr1 又早早的完成任务而无所事事。

从上面的结果可以看出,任务是平均分配的。

也就是说,不管你上个任务是否完成,我继续把后面的任务分发给你,而实际上为了效率,谁消费得越快,谁就得到越多。因此可以通过 BasicQos 方法的参数设为 1,前提是在手动 ack 的情况下才生效 ,即 autoAck = false。实现能者多劳。

java 复制代码
package cc.wj.test.rabbitmq.learn.work;

import cc.wj.test.rabbitmq.learn.MqConst;
import com.rabbitmq.client.*;

import java.io.IOException;
import java.util.concurrent.TimeUnit;

/**
 * @author wj
 * @version 1.0
 * @date 2026/1/21 16:13
 */
public class FooConsumer2 {
    public static void main(String[] args) throws Exception {

        // 1. 创建连接工厂以及参数配置
        ConnectionFactory factory = new ConnectionFactory();
        factory.setHost("192.168.146.130");
        factory.setPort(5672);
        factory.setVirtualHost("/");
        factory.setUsername("guest");
        factory.setPassword("guest");

        // 2. 创建连接Connection
        Connection connection = factory.newConnection();

        // 3. 创建管道Channel
        Channel channel = connection.createChannel();
        channel.basicQos(5);

        // 4. 创建队列Queue(简单模式不需要交换机Exchange)
        /**
         * queue: 队列名
         * durable: 是否持久化,true:重启后,队列依然存在,否则不存在
         * exclusive: 是否独占,true:只能有一个消费者监听这个队列,一般设置为false
         * autoDelete: 是否自动删除,true:当没有消费者的时候,自动删除队列
         * arguments: map类型其他参数
         */
        channel.queueDeclare(MqConst.QUEUE_NAME_WORK, true, false, false, null);

        // 5. 监听并消费消息
        /**
         * queue: 监听的队列名
         * autoAck: 是否自动确认,true:告知mq消费者已经消费的确认通知
         * callback: 回调函数,处理监听的消息
         */
        Consumer consumer = new DefaultConsumer(channel) {
            /**
             * 重写消息配送方法
             *
             * @param consumerTag: 消息标签(标识)
             * @param envelope:    信封(一些信息,比如交换机路由等信息)
             * @param properties:  配置信息,和生产者的一致
             * @param body:        消息数据
             * @throws IOException
             */
            @Override
            public void handleDelivery(String consumerTag,
                                       Envelope envelope,
                                       AMQP.BasicProperties properties,
                                       byte[] body) throws IOException {
                try {
                    TimeUnit.SECONDS.sleep(2);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
                System.out.println("body = 消费者2  :" + new String(body));

                //确认消息
                channel.basicAck(envelope.getDeliveryTag(),false);
            }
        };
        channel.basicConsume(MqConst.QUEUE_NAME_WORK, false, consumer);

        // 不需要关闭连接,则持续监听
    }
}

3.发布/订阅Publish/Subscribe-Fanout

一次向多个消费者发送消息,该模式的交换机类型为 Fanout,也称为广播。

它具有以下性质:

  • 可以有多个消费者

  • 每个消费者有自己的 queue。

  • 每个队列都要绑定到 Exchange。

  • 生产者发送的消息,只能发送到交换机,交换机来决定要发给哪个队列,生产者无法决定

  • 交换机把消息发送给绑定过的所有队列。

  • 队列的消费者都能拿到消息,实现一条消息被多个消费者消费

  • 适合日志广播、事件通知

只要监听队列,所有的消费者都能消费同一个消息,这类似与公众号的订阅,比如我和你都订阅了某某的公众号,某某只要发布一个新的文章,那么我们都可以同时收到,这就是发布订阅模式。

需要注意,这里使用了交换机,因为不同的用户组可以订阅不同的队列,所以通过交换机来绑定管理并且把消息路由到不同的队列即可。


交换机的类型:

  • Fanout:广播模式,把消息发送给所有绑定的队列

  • Direct:定向 投递,把消息发送给指定的routing key的队列

  • Topic:通配符模式,把消息交给符合routing pattern的队列
    需要注意,交换机只负责转发消息,不会存储消息,存储消息的职责是队列的


    java 复制代码
    package cc.wj.test.rabbitmq.learn.publish;
    
    import cc.wj.test.rabbitmq.learn.MqConst;
    import com.rabbitmq.client.BuiltinExchangeType;
    import com.rabbitmq.client.Channel;
    import com.rabbitmq.client.Connection;
    import com.rabbitmq.client.ConnectionFactory;
    
    import static cc.wj.test.rabbitmq.learn.MqConst.EXCHANGE_NAME_PUBLISH;
    
    /**
     * 简单模式
     *
     * @author wj
     * @version 1.0
     * @date 2026/1/21 16:11
     */
    public class PublishProducer {
        public static void main(String[] args) throws Exception {
    
            // 1. 创建连接工厂以及参数配置
            ConnectionFactory factory = new ConnectionFactory();
            factory.setHost("192.168.146.130");
            factory.setPort(5672);
            factory.setVirtualHost("/");
            factory.setUsername("guest");
            factory.setPassword("guest");
    
            // 2. 创建连接Connection
            Connection connection = factory.newConnection();
    
            // 3. 创建管道Channel
            Channel channel = connection.createChannel();
    
            // 创建交换机
            /**
             * exchange: 交换机名称
             * type: 交换机类型
             *      FANOUT("fanout"): 广播模式,把消息发送给所有绑定的队列
             *      DIRECT("direct"): 定向投递,把消息发送给指定的`routing key`的队列
             *      TOPIC("topic"): 通配符模式,把消息交给符合`routing pattern`的队列
             *      HEADERS("headers"): 使用率不多,参数匹配
             * durable: 是否持久化
             * autoDelete: 是否自动删除
             * internal: 内部意思,true:表示当前Exchange是RabbitMQ内部使用,用户创建的队列不会消费该类型交换机下的消息,所以我们自己使用设置为false即可
             * arguments: 参数
             */
            channel.exchangeDeclare(EXCHANGE_NAME_PUBLISH,
                    BuiltinExchangeType.FANOUT, true, false, false, null);
    
            // 4. 创建队列Queue
            /**
             * queue: 队列名
             * durable: 是否持久化,true:重启后,队列依然存在,否则不存在
             * exclusive: 是否独占,true:只能有一个消费者监听这个队列,一般设置为false
             * autoDelete: 是否自动删除,true:当没有消费者的时候,自动删除队列
             * arguments: map类型其他参数
             */
            channel.queueDeclare(MqConst.QUEUE_NAME_PUBLISH1, true, false, false, null);
            channel.queueDeclare(MqConst.QUEUE_NAME_PUBLISH2, true, false, false, null);
            // 绑定交换机和队列
            /**
             * queue
             * exchange
             * routingKey: 路由key,绑定规则,这里暂不使用(fanout本身广播给所有订阅者,所以没有路由规则,使用空字符串即可)
             */
            channel.queueBind(MqConst.QUEUE_NAME_PUBLISH1, EXCHANGE_NAME_PUBLISH, "");
            channel.queueBind(MqConst.QUEUE_NAME_PUBLISH2, EXCHANGE_NAME_PUBLISH, "");
            // 5. 向队列发送消息
            /**
             * exchange: 交换机名称,简单模式没有,直接设置为 ""
             * routingKey: 路由key,映射路径,如果交换机是默认"",则路由key和队列名一致
             * props: 配置信息
             * body: 消息数据
             */
            for (int i = 0; i < 20; i++) {
                String msg = "Hello Work~" + i;
                channel.basicPublish(EXCHANGE_NAME_PUBLISH, "", null, msg.getBytes());
            }
    
            // 7. 释放
            channel.close();
            connection.close();
        }
    }

    消费者:

java 复制代码
package cc.wj.test.rabbitmq.learn.publish;

import cc.wj.test.rabbitmq.learn.MqConst;
import com.rabbitmq.client.*;

import java.io.IOException;

/**
 * @author wj
 * @version 1.0
 * @date 2026/1/21 16:13
 */
public class PublishConsumer1 {
    public static void main(String[] args) throws Exception {

        // 1. 创建连接工厂以及参数配置
        ConnectionFactory factory = new ConnectionFactory();
        factory.setHost("192.168.146.130");
        factory.setPort(5672);
        factory.setVirtualHost("/");
        factory.setUsername("guest");
        factory.setPassword("guest");

        // 2. 创建连接Connection
        Connection connection = factory.newConnection();

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

        // 4. 创建队列Queue(简单模式不需要交换机Exchange)
        /**
         * queue: 队列名
         * durable: 是否持久化,true:重启后,队列依然存在,否则不存在
         * exclusive: 是否独占,true:只能有一个消费者监听这个队列,一般设置为false
         * autoDelete: 是否自动删除,true:当没有消费者的时候,自动删除队列
         * arguments: map类型其他参数
         */
        channel.queueDeclare(MqConst.QUEUE_NAME_PUBLISH1, true, false, false, null);

        // 5. 监听并消费消息
        /**
         * queue: 监听的队列名
         * autoAck: 是否自动确认,true:告知mq消费者已经消费的确认通知
         * callback: 回调函数,处理监听的消息
         */
        Consumer consumer = new DefaultConsumer(channel) {
            /**
             * 重写消息配送方法
             *
             * @param consumerTag: 消息标签(标识)
             * @param envelope:    信封(一些信息,比如交换机路由等信息)
             * @param properties:  配置信息,和生产者的一致
             * @param body:        消息数据
             * @throws IOException
             */
            @Override
            public void handleDelivery(String consumerTag,
                                       Envelope envelope,
                                       AMQP.BasicProperties properties,
                                       byte[] body) throws IOException {
                System.out.println("body = 消费者1  :" + new String(body));

                super.handleDelivery(consumerTag, envelope, properties, body);
            }
        };
        channel.basicConsume(MqConst.QUEUE_NAME_PUBLISH1, true, consumer);

        // 不需要关闭连接,则持续监听
    }
}

|----------------------------------------------------------------------------|----------------------------------------------------------------------------|
| | |

4.路由Routing-Direct

选择性接收消息。

在 Direct 模型下,队列与交换机的绑定,不能是任意绑定了,而是要指定一个 RoutingKey(路由 key),消息的发送方在向 Exchange 发送消息时,也必须指定消息的 routing key。

  • P:生产者,向 Exchange 发送消息,发送消息时,会指定一个 routing key。

  • X:Exchange,接收生产者的消息,然后把消息递交给 与 routing key 完全匹配的队列。

  • C1:消费者,其所在队列指定了需要 routing key 为 error 的消息。

  • C2:消费者,其所在队列指定了需要 routing key 为 info、error、warning 的消息。

特点:

  • Direct Exchange

  • 精确匹配路由键

  • 适合分类处理

实现示例:

复制代码
Exchange: direct_logs
          ├── Queue1 绑定键: error
          ├── Queue2 绑定键: warning
          └── Queue3 绑定键: info, error, warning

生产者:

java 复制代码
package cc.wj.test.rabbitmq.learn.route;

import cc.wj.test.rabbitmq.learn.MqConst;
import com.rabbitmq.client.BuiltinExchangeType;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;

import static cc.wj.test.rabbitmq.learn.MqConst.*;

/**
 * 路由模式
 *
 * @author wj
 * @version 1.0
 * @date 2026/1/21 16:11
 */
public class RouteProducer {
    public static void main(String[] args) throws Exception {

        // 1. 创建连接工厂以及参数配置
        ConnectionFactory factory = new ConnectionFactory();
        factory.setHost("192.168.146.130");
        factory.setPort(5672);
        factory.setVirtualHost("/");
        factory.setUsername("guest");
        factory.setPassword("guest");

        // 2. 创建连接Connection
        Connection connection = factory.newConnection();

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

        // 创建交换机
        /**
         * exchange: 交换机名称
         * type: 交换机类型
         *      FANOUT("fanout"): 广播模式,把消息发送给所有绑定的队列
         *      DIRECT("direct"): 定向投递,把消息发送给指定的`routing key`的队列
         *      TOPIC("topic"): 通配符模式,把消息交给符合`routing pattern`的队列
         *      HEADERS("headers"): 使用率不多,参数匹配
         * durable: 是否持久化
         * autoDelete: 是否自动删除
         * internal: 内部意思,true:表示当前Exchange是RabbitMQ内部使用,用户创建的队列不会消费该类型交换机下的消息,所以我们自己使用设置为false即可
         * arguments: 参数
         */
        channel.exchangeDeclare(EXCHANGE_NAME_ROUTE,
                BuiltinExchangeType.DIRECT, true, false, false, null);

        // 4. 创建队列Queue
        /**
         * queue: 队列名
         * durable: 是否持久化,true:重启后,队列依然存在,否则不存在
         * exclusive: 是否独占,true:只能有一个消费者监听这个队列,一般设置为false
         * autoDelete: 是否自动删除,true:当没有消费者的时候,自动删除队列
         * arguments: map类型其他参数
         */
        channel.queueDeclare(MqConst.QUEUE_NAME_ROUTE1, true, false, false, null);
        channel.queueDeclare(MqConst.QUEUE_NAME_ROUTE2, true, false, false, null);
        channel.queueDeclare(MqConst.QUEUE_NAME_ROUTE3, true, false, false, null);

        // 绑定交换机和队列
        /**
         * queue
         * exchange
         * routingKey: 路由key,绑定规则,这里暂不使用(fanout本身广播给所有订阅者,所以没有路由规则,使用空字符串即可)
         */
        channel.queueBind(MqConst.QUEUE_NAME_ROUTE1, EXCHANGE_NAME_ROUTE, ERROR);
        channel.queueBind(MqConst.QUEUE_NAME_ROUTE2, EXCHANGE_NAME_ROUTE, WARNING);

        channel.queueBind(MqConst.QUEUE_NAME_ROUTE3, EXCHANGE_NAME_ROUTE, INFO);
        channel.queueBind(MqConst.QUEUE_NAME_ROUTE3, EXCHANGE_NAME_ROUTE, WARNING);
        channel.queueBind(MqConst.QUEUE_NAME_ROUTE3, EXCHANGE_NAME_ROUTE, ERROR);
        // 5. 向队列发送消息
        /**
         * exchange: 交换机名称,简单模式没有,直接设置为 ""
         * routingKey: 路由key,映射路径,如果交换机是默认"",则路由key和队列名一致
         * props: 配置信息
         * body: 消息数据
         */
        String[] logArr = {INFO, ERROR, WARNING};
        for (int i = 0; i < 10; i++) {
            String msg = "Hello Work~route~~~~" + i + "~~~~" + logArr[i % 3];
            channel.basicPublish(EXCHANGE_NAME_ROUTE, logArr[i % 3], null, msg.getBytes());
        }

        // 7. 释放
        channel.close();
        connection.close();
    }
}

消费者:

java 复制代码
package cc.wj.test.rabbitmq.learn.route;

import cc.wj.test.rabbitmq.learn.MqConst;
import com.rabbitmq.client.*;

import java.io.IOException;

/**
 * @author wj
 * @version 1.0
 * @date 2026/1/21 16:13
 */
public class RouteConsumer1 {
    public static void main(String[] args) throws Exception {

        // 1. 创建连接工厂以及参数配置
        ConnectionFactory factory = new ConnectionFactory();
        factory.setHost("192.168.146.130");
        factory.setPort(5672);
        factory.setVirtualHost("/");
        factory.setUsername("guest");
        factory.setPassword("guest");

        // 2. 创建连接Connection
        Connection connection = factory.newConnection();

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

        // 4. 创建队列Queue(简单模式不需要交换机Exchange)
        /**
         * queue: 队列名
         * durable: 是否持久化,true:重启后,队列依然存在,否则不存在
         * exclusive: 是否独占,true:只能有一个消费者监听这个队列,一般设置为false
         * autoDelete: 是否自动删除,true:当没有消费者的时候,自动删除队列
         * arguments: map类型其他参数
         */
        channel.queueDeclare(MqConst.QUEUE_NAME_ROUTE1, true, false, false, null);

        // 5. 监听并消费消息
        /**
         * queue: 监听的队列名
         * autoAck: 是否自动确认,true:告知mq消费者已经消费的确认通知
         * callback: 回调函数,处理监听的消息
         */
        Consumer consumer = new DefaultConsumer(channel) {
            /**
             * 重写消息配送方法
             *
             * @param consumerTag: 消息标签(标识)
             * @param envelope:    信封(一些信息,比如交换机路由等信息)
             * @param properties:  配置信息,和生产者的一致
             * @param body:        消息数据
             * @throws IOException
             */
            @Override
            public void handleDelivery(String consumerTag,
                                       Envelope envelope,
                                       AMQP.BasicProperties properties,
                                       byte[] body) throws IOException {
                System.out.println("body = 消费者1  :" + new String(body));

                super.handleDelivery(consumerTag, envelope, properties, body);
            }
        };
        channel.basicConsume(MqConst.QUEUE_NAME_ROUTE1, true, consumer);

        // 不需要关闭连接,则持续监听
    }
}

其他两个消费者类似,换队列名。

|----------------------------------------------------------------------------|----------------------------------------------------------------------------|----------------------------------------------------------------------------|
| | | |

5.Topics-topic

Topic 类型的 Exchange 与 Direct 相比,都是可以根据 RoutingKey 把消息路由到不同的队列。只不过 Topic 类型 Exchange 可以让队列在绑定 Routing key 的时候使用通配符!基于模式的路由,最灵活。

  • #:匹配一个或多个词

  • *:匹配一个词

java 复制代码
user.#  # 可以匹配到 user.add  user.add.batch
user.*  # 只能匹配到 user.add ,不能匹配到 user.add.batch
复制代码
public static String TOPIC_PAY = "*.pay.*";//QUEUE_NAME_TOPIC1
public static String TOPIC_ORDER = "*.*.order";//QUEUE_NAME_TOPIC2
public static String TOPIC_GOODS = "goods.#";//QUEUE_NAME_TOPIC2

生产者:

java 复制代码
package cc.wj.test.rabbitmq.learn.topic;

import cc.wj.test.rabbitmq.learn.MqConst;
import com.rabbitmq.client.BuiltinExchangeType;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;

import static cc.wj.test.rabbitmq.learn.MqConst.*;

/**
 * 通配符模式
 *
 * @author wj
 * @version 1.0
 * @date 2026/1/21 16:11
 */
public class TopicProducer {
    public static void main(String[] args) throws Exception {

        // 1. 创建连接工厂以及参数配置
        ConnectionFactory factory = new ConnectionFactory();
        factory.setHost("192.168.146.130");
        factory.setPort(5672);
        factory.setVirtualHost("/");
        factory.setUsername("guest");
        factory.setPassword("guest");

        // 2. 创建连接Connection
        Connection connection = factory.newConnection();

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

        // 创建交换机
        /**
         * exchange: 交换机名称
         * type: 交换机类型
         *      FANOUT("fanout"): 广播模式,把消息发送给所有绑定的队列
         *      DIRECT("direct"): 定向投递,把消息发送给指定的`routing key`的队列
         *      TOPIC("topic"): 通配符模式,把消息交给符合`routing pattern`的队列
         *      HEADERS("headers"): 使用率不多,参数匹配
         * durable: 是否持久化
         * autoDelete: 是否自动删除
         * internal: 内部意思,true:表示当前Exchange是RabbitMQ内部使用,用户创建的队列不会消费该类型交换机下的消息,所以我们自己使用设置为false即可
         * arguments: 参数
         */
        channel.exchangeDeclare(EXCHANGE_NAME_TOPIC,
                BuiltinExchangeType.TOPIC, true, false, false, null);

        // 4. 创建队列Queue
        /**
         * queue: 队列名
         * durable: 是否持久化,true:重启后,队列依然存在,否则不存在
         * exclusive: 是否独占,true:只能有一个消费者监听这个队列,一般设置为false
         * autoDelete: 是否自动删除,true:当没有消费者的时候,自动删除队列
         * arguments: map类型其他参数
         */
        channel.queueDeclare(QUEUE_NAME_TOPIC1, true, false, false, null);
        channel.queueDeclare(MqConst.QUEUE_NAME_TOPIC2, true, false, false, null);
        // 绑定交换机和队列
        /**
         * queue
         * exchange
         * routingKey: 路由key,绑定规则,这里暂不使用(fanout本身广播给所有订阅者,所以没有路由规则,使用空字符串即可)
         */
        channel.queueBind(MqConst.QUEUE_NAME_TOPIC1, EXCHANGE_NAME_TOPIC, TOPIC_PAY);
        channel.queueBind(MqConst.QUEUE_NAME_TOPIC2, EXCHANGE_NAME_TOPIC, TOPIC_ORDER);
        channel.queueBind(MqConst.QUEUE_NAME_TOPIC2, EXCHANGE_NAME_TOPIC, TOPIC_GOODS);
        // 5. 向队列发送消息
        /**
         * exchange: 交换机名称,简单模式没有,直接设置为 ""
         * routingKey: 路由key,映射路径,如果交换机是默认"",则路由key和队列名一致
         * props: 配置信息
         * body: 消息数据
         */

        String[] topicKeys = {
                "user.pay.success",      // 用户支付成功
                "order.pay.failed",      // 订单支付失败
                "system.pay.notify.user",      // 系统支付通知
                "create.user.order",     // 创建用户订单
                "update.system.order",   // 更新系统订单
                "cancel.shop.order",      // 取消商店订单
                "goods",                 // 商品(最简单)
                "goods.detail.info"      // 商品详情信息
        };
        for (int i = 0; i < 10; i++) {
            String msg = "Hello Work~topic~~~~" + topicKeys[i%8];
            channel.basicPublish(EXCHANGE_NAME_TOPIC, topicKeys[i%8], null, msg.getBytes());
        }

        // 7. 释放
        channel.close();
        connection.close();
    }
}

消费者:

java 复制代码
package cc.wj.test.rabbitmq.learn.topic;

import cc.wj.test.rabbitmq.learn.MqConst;
import com.rabbitmq.client.*;

import java.io.IOException;

/**
 * @author wj
 * @version 1.0
 * @date 2026/1/21 16:13
 */
public class TopicConsumer1 {
    public static void main(String[] args) throws Exception {

        // 1. 创建连接工厂以及参数配置
        ConnectionFactory factory = new ConnectionFactory();
        factory.setHost("192.168.146.130");
        factory.setPort(5672);
        factory.setVirtualHost("/");
        factory.setUsername("guest");
        factory.setPassword("guest");

        // 2. 创建连接Connection
        Connection connection = factory.newConnection();

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

        // 4. 创建队列Queue(简单模式不需要交换机Exchange)
        /**
         * queue: 队列名
         * durable: 是否持久化,true:重启后,队列依然存在,否则不存在
         * exclusive: 是否独占,true:只能有一个消费者监听这个队列,一般设置为false
         * autoDelete: 是否自动删除,true:当没有消费者的时候,自动删除队列
         * arguments: map类型其他参数
         */
        channel.queueDeclare(MqConst.QUEUE_NAME_TOPIC1, true, false, false, null);

        // 5. 监听并消费消息
        /**
         * queue: 监听的队列名
         * autoAck: 是否自动确认,true:告知mq消费者已经消费的确认通知
         * callback: 回调函数,处理监听的消息
         */
        Consumer consumer = new DefaultConsumer(channel) {
            /**
             * 重写消息配送方法
             *
             * @param consumerTag: 消息标签(标识)
             * @param envelope:    信封(一些信息,比如交换机路由等信息)
             * @param properties:  配置信息,和生产者的一致
             * @param body:        消息数据
             * @throws IOException
             */
            @Override
            public void handleDelivery(String consumerTag,
                                       Envelope envelope,
                                       AMQP.BasicProperties properties,
                                       byte[] body) throws IOException {
                System.out.println("body = 消费者1  :" + new String(body));

                super.handleDelivery(consumerTag, envelope, properties, body);
            }
        };
        channel.basicConsume(MqConst.QUEUE_NAME_TOPIC1, true, consumer);

        // 不需要关闭连接,则持续监听
    }
}

其他另一个消费者类似,换队列名。

部分内容引用:https://xie.infoq.cn/article/f0cb0dc4d0e252e4573348ba2

相关推荐
沛沛老爹1 小时前
Web开发者转型AI安全核心:Agent Skills沙盒环境与威胁缓解实战
java·前端·人工智能·安全·rag·web转型升级
像少年啦飞驰点、1 小时前
Java大厂面试真题:Spring Boot + Kafka + Redis 在电商场景下的实战应用
java·spring boot·redis·分布式·kafka·面试题·电商秒杀
小李广1 小时前
修改MS源码—开发新接口:查询所有项目下面的模块
java·linux·服务器
CHrisFC1 小时前
环境第三方检测机构LIMS系统选型:从合规基础到效率制胜
java·大数据·人工智能
么么...1 小时前
布隆过滤器详解:原理、实现与应用
java·数据结构·哈希算法·散列表
☀Mark_LY2 小时前
java读取excel文件返回JSON
java·json·excel
ITUnicorn2 小时前
【Vue2+SpringBoot在线商城】13-本项目运用到的技术
java·spring boot·redis·后端
仰泳之鹅2 小时前
【杂谈】C语言中的链接属性、声明周期以及static关键字
java·c语言·前端
weixin_531651812 小时前
Java 正则表达式
java·正则表达式