Rabbitmq学习笔记(一)

152 阅读11分钟

一、架构图

rabbit五种模式你了解吗?一学就会的代码不确定不来看看吗?

image.png rabbitmq由以下组成部分构成:

  • Producer:生产者
  • Connection:连接器。内部有各个channel,比Connection更轻量
  • Broker:mq的核心部分。内部的各个虚拟机彼此独立、隔离
  • Virtual Host:虚拟机。内部有交换机和队列,交换机用于分发消息到不同队列中,一个交换机可以绑定到多个队列,不同的路由key决定了消息将被发送到哪个queue
  • Consumer:消费者

分为五个模式(具体可见官网www.rabbitmq.com/getstarted.…

1. hello world模式

最简单的模式,生产者直接将消息发到队列中,消费者直接从队列中拉取消息进行消费。

image.png

2. work queue模式

工作队列模式:生产者将消息发送到队列中,多个消费者去竞争进行消费。同一条消息只会被消费一次,主要应用于任务较多时提高消费效率。

image.png

3. 发布-订阅模式(pub-sub)

这个模型主要的变化在于多了一个交换机的组件和队列进行绑定(routing key为"",相当于和所有队列都绑定)。交换机有两个功能:

  • 接收来自生产者的消息
  • 对消息进行处理,如丢弃、交给某个队列、交给全部队列等。

交换机主要有三种类型:

  • Fanout:广播
  • Direct:直接定向传输到指定的routing key的队列
  • Topic:通配符,把消息传输给符合routing pattern的队列

需要注意的是,Exchange本身只转发消息,并不储存消息。所以如果没有queue与exchange绑定,或者路由规则均不符合,消息将没法发送,导致消息会丢失。

image.png

4. 路由模式(routing)

  • 此模式下,队列和交换机的绑定需要指定routing key,而不是全都进行绑定
  • 在生产者发送消息时,也需要指定routing key
  • 交换机的转发不再是广播,而是根据消息的routing key进行选择转发。只有消息的key和队列的key一致,channel中才会收到来自exchange的消息。
image.png

5. 主题模式(topic)

  • 网上花里胡哨的说辞一堆,其实就是定向模式本身改为了通配符进行模糊匹配。
  • 定向模式和主题模式的exchange在声明和channel的绑定关系时需要指定routing key,在投递消息给exchange时也需要指定routing key。当二者匹配成功,消息被投递到channel中,进而发到queue中。
image.png

二、安装rabbitmq

$ docker search rabbit
$ docker pull rabbitmq
$ docker run -e RABBITMQ_DEFAULT_USER=guest -e RABBITMQ_DEFAULT_PASS=guest --name mq --hostname localhost -p 15672:15672 -p 5672:5672 -d rabbitmq

image.png

安装好rabbitmq并启动后,可以通过访问localhost:15672进入mq的web界面。用户名、密码均为guest。 注意:15672是访问web界面的端口,5672是程序连接mq的端口,千万别弄混。

image.png 初次进入时无法访问,原因是rabbitmq默认web界面管理插件是关闭的,通过命令开启即可 解决方法:进入容器中打开rabbitmq_management,重新刷新浏览器

$ docker exec -it 65d4c09220f5 /bin/bash
root@localhost:/# rabbitmq-plugins enable rabbitmq_management

image.png

有可能,进去web界面后看不到消息条数,或者点击交换机想查看详细信息,报错:Stats in management UI are disabled on this node 解决方法:进入docker容器中修改mq的conf文件,然后重启docker容器,刷新页面后即可恢复

---以下内容在外部机器中----
$ docker exec -it 65d4c09220f5a615a3add30944db27b5ef50efb857003eab59cf0d1dd6e14651 /bin/bash

---以下内容在容器中敲----
root@localhost:/# cd /etc/rabbitmq/conf.d/
root@localhost:/etc/rabbitmq/conf.d# vim management_agent.disable_metrics_collector.conf

----以下内容在vim中---
management_agent.disable_metrics_collector = false(然后保存,并退出到容器外)

---以下内容在外部机器中----
$ docker restart 65d4c09220f5

三、各个模式的代码实现

各个模式的实现思路已经按顺序写在注释中了,可见注释中的序号。

1. hello world模式

1)生产者

package com.msq.producer;

import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;

import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.concurrent.TimeoutException;

/**
 * 用于发送消息
 * Author: masiqi
 * Created on: 2022/10/02
 */
public class Producer_HelloWorld {

    public static void main(String[] args) throws IOException, TimeoutException {

        //1. 创建连接工厂(用于新建连接)
        ConnectionFactory factory = new ConnectionFactory();

        //2. 设置参数
        factory.setHost("127.0.0.1");
        factory.setPort(5672);  // 默认值:5672
        factory.setVirtualHost("/msq"); // 虚拟机 默认值:/
        factory.setUsername("guest");  //  默认值:guest
        factory.setPassword("guest");  //  默认值:guest

        //3. 创建Connection
        Connection connection = factory.newConnection();

        //4. 创建Channel(一个Connection中有多个channel)
        Channel channel = connection.createChannel();

        //5. 创建队列Queue(hello_world模式下没有exchange,所以直接创建queue即可 )
        /**
         * 各个参数含义:
         * - 队列名
         * - 是否持久化
         * - 是否独占:只能有一个消费者监听队列;当Connection关闭时,是否删除队列
         * - 是否自动删除:当没有Consumer时,自动删除
         * - 其余参数
         * !!! 如果没有一个名叫hello_world的队列,则会创建队列;否则就不会创建,直接使用
         */
        channel.queueDeclare("hello_world", true, false, false, null);

        String message = "hello rabbitmq~~~";
        //6. 发送消息
        /**
         * - 交换机名称
         * - 路由键名称
         * - 配置信息
         * - 消息数据(字节数据)
         */
        channel.basicPublish("", "hello_world", null, message.getBytes(StandardCharsets.UTF_8));

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

发送报错:原因是不存在对应的虚拟机。在管理界面中添加虚拟机后报错就会消失 image.png 解决方法:www.cnblogs.com/max1995/p/1…

2)消费者

package com.msq.consumer;

import com.rabbitmq.client.*;

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

/**
 * Author: masiqi
 * Created on: 2022/10/03
 */
public class Consumer_HelloWorld {

    public static void main(String[] args) throws IOException, TimeoutException {
        //1. 创建连接工厂(用于新建连接)
        ConnectionFactory factory = new ConnectionFactory();

        //2. 设置参数
        factory.setHost("127.0.0.1");
        factory.setPort(5672);  // 默认值:5672
        factory.setVirtualHost("/msq"); // 虚拟机 默认值:/
        factory.setUsername("guest");  //  默认值:guest
        factory.setPassword("guest");  //  默认值:guest

        //3. 创建Connection
        Connection connection = factory.newConnection();

        //4. 创建Channel(一个Connection中有多个channel)
        Channel channel = connection.createChannel();

        //5. 创建队列Queue(hello_world模式下没有exchange,所以直接创建queue即可 )
        /**
         * 各个参数含义:
         * - 队列名
         * - 是否持久化
         * - 是否独占:只能有一个消费者监听队列;当Connection关闭时,是否删除队列
         * - 是否自动删除:当没有Consumer时,自动删除
         * - 其余参数
         * !!! 如果没有一个名叫hello_world的队列,则会创建队列;否则就不会创建,直接使用
         */
        channel.queueDeclare("hello_world", true, false, false, null);

        //6. 发送消息
        Consumer consumer = new DefaultConsumer(channel){
            /**
             * 回调方法,收到消息后会自动调用
             * @param consumerTag:消息标识
             * @param envelope:获取信息,交换机、路由key等
             * @param properties:配置信息
             * @param body:消息数据
             */
            @Override
            public void handleDelivery(String consumerTag,
                                       Envelope envelope,
                                       AMQP.BasicProperties properties,
                                       byte[] body){
                System.out.println("consumerTag:" + consumerTag);
                System.out.println("exchange:" + envelope.getExchange());
                System.out.println("RoutingKey:" +  envelope.getRoutingKey());
                System.out.println("收到的消息:" + new String(body));
            }
        };
        /**
         * 队列名
         * 是否自动确认
         * 回调函数
         */
        channel.basicConsume("hello_world", true, consumer);

        // 7.不关闭资源

    }
}

2. work-queue模式

1)生产者

package producer;

import com.rabbitmq.client.BuiltinExchangeType;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;

import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.concurrent.TimeoutException;

/**
 * Author: masiqi
 * Created on: 2022/10/03
 */
public class Producer_PubSub {

    public static void main(String[] args) throws IOException, TimeoutException {
        // 1.创建工厂
        ConnectionFactory factory = new ConnectionFactory();

        // 2.设置参数
        factory.setHost("localhost");
        factory.setVirtualHost("/msq");

        // 3.创建connection
        Connection connection = factory.newConnection();

        // 4.创建channel
        Channel channel = connection.createChannel();
        /**
         * 1.exchange:交换机名称
         * 2.type:交换机类型
         *      - Direct:定向
         *      - Fanout:广播
         *      - Topic:通配符
         *      - Headers:参数匹配(用得少)
         * 3.durable:是否持久化
         * 4.autoDelete:是否自动删除
         * 5.internal:是否内部使用,一般赋值false
         * 6.arguments:参数
         */
        // 5.创建exchange
        String exchangeName = "test_fanout";
        channel.exchangeDeclare(exchangeName, BuiltinExchangeType.FANOUT, true, false, false, null);

        // 6.创建queue
        channel.queueDeclare("fanout_queue1", true, false, false, null);
        channel.queueDeclare("fanout_queue2", true, false, false, null);

        // 7.绑定queue和exchange
        /**
         * 1.queue:队列名称
         * 2.exchange:交换机名称
         * 3.routingkey:路由键,绑定规则
         *      - exchange的type为FANOUT,则key为""
         *      -
         */
        channel.queueBind("fanout_queue1", exchangeName, "");
        channel.queueBind("fanout_queue2", exchangeName, "");

        String message = "2022-10-03 msq在学习rabbitmq";
        channel.basicPublish(exchangeName, "", null, message.getBytes(StandardCharsets.UTF_8));
        channel.close();
        connection.close();
    }
}

2)消费者

消费者1:使用fanout_queue1队列

package com.msq.consumer;

import com.rabbitmq.client.*;

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

/**
 * Author: masiqi
 * Created on: 2022/10/03
 */
public class Consumer_WorkQueue1 {

    public static void main(String[] args) throws IOException, TimeoutException {
        ConnectionFactory factory = new ConnectionFactory();
        factory.setHost("localhost");
        factory.setVirtualHost("/msq");
        Connection connection = factory.newConnection();
        Channel channel = connection.createChannel();
        channel.queueDeclare("work_queue", true, false, false, null);

        Consumer consumer = new DefaultConsumer(channel){
            @Override
            public void handleDelivery(String consumerTag,
                                       Envelope envelope,
                                       AMQP.BasicProperties properties,
                                       byte[] body){
                System.out.println("收到的消息:" + new String(body));
            }
        };
        channel.basicConsume("work_queue", true, consumer);

    }
}

消费者2

package com.msq.consumer;

import com.rabbitmq.client.*;

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

/**
 * Author: masiqi
 * Created on: 2022/10/03
 */
public class Consumer_WorkQueue2 {

    public static void main(String[] args) throws IOException, TimeoutException {
        ConnectionFactory factory = new ConnectionFactory();
        factory.setHost("localhost");
        factory.setVirtualHost("/msq");
        Connection connection = factory.newConnection();
        Channel channel = connection.createChannel();
        channel.queueDeclare("work_queue", true, false, false, null);

        Consumer consumer = new DefaultConsumer(channel){
            @Override
            public void handleDelivery(String consumerTag,
                                       Envelope envelope,
                                       AMQP.BasicProperties properties,
                                       byte[] body){
                System.out.println("收到的消息:" + new String(body));
            }
        };
        channel.basicConsume("work_queue", true, consumer);

    }
}

3. pub-sub模式

1)生产者

package producer;

import com.rabbitmq.client.BuiltinExchangeType;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;

import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.concurrent.TimeoutException;

/**
 * Author: masiqi
 * Created on: 2022/10/03
 */
public class Producer_PubSub {

    public static void main(String[] args) throws IOException, TimeoutException {
        // 1.创建工厂
        ConnectionFactory factory = new ConnectionFactory();

        // 2.设置参数
        factory.setHost("localhost");
        factory.setVirtualHost("/msq");

        // 3.创建connection
        Connection connection = factory.newConnection();

        // 4.创建channel
        Channel channel = connection.createChannel();
        /**
         * 1.exchange:交换机名称
         * 2.type:交换机类型
         *      - Direct:定向
         *      - Fanout:广播
         *      - Topic:通配符
         *      - Headers:参数匹配(用得少)
         * 3.durable:是否持久化
         * 4.autoDelete:是否自动删除
         * 5.internal:是否内部使用,一般赋值false
         * 6.arguments:参数
         */
        // 5.创建exchange
        String exchangeName = "test_fanout";
        channel.exchangeDeclare(exchangeName, BuiltinExchangeType.FANOUT, true, false, false, null);

        // 6.创建queue
        channel.queueDeclare("fanout_queue1", true, false, false, null);
        channel.queueDeclare("fanout_queue2", true, false, false, null);

        // 7.绑定queue和exchange
        /**
         * 1.queue:队列名称
         * 2.exchange:交换机名称
         * 3.routingkey:路由键,绑定规则
         *      - exchange的type为FANOUT,则key为""
         *      -
         */
        channel.queueBind("fanout_queue1", exchangeName, "");
        channel.queueBind("fanout_queue2", exchangeName, "");

        String message = "2022-10-03 msq在学习rabbitmq";
        channel.basicPublish(exchangeName, "", null, message.getBytes(StandardCharsets.UTF_8));
        channel.close();
        connection.close();
    }
}

2)消费者

消费者1:

package com.msq.consumer;

import com.rabbitmq.client.*;

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

/**
 * Author: masiqi
 * Created on: 2022/10/04
 */
public class Consumer_PubSub1 {

    public static void main(String[] args) throws IOException, TimeoutException {
        ConnectionFactory factory = new ConnectionFactory();
        factory.setHost("localhost");
        factory.setVirtualHost("/msq");
        Connection connection = factory.newConnection();
        Channel channel = connection.createChannel();
//        channel.queueDeclare("fanout_queue1", true, false, false, null);  无需重复声明
        Consumer consumer = new DefaultConsumer(channel){
            @Override
            public void handleDelivery(String consumerTag,
                                       Envelope envelope,
                                       AMQP.BasicProperties properties,
                                       byte[] body){
                System.out.println("收到的消息:" + new String(body));
                System.out.println("消费者1将保存此信息到数据库...");
            }
        };
        channel.basicConsume("fanout_queue1", true, consumer);
    }
}

消费者2:

package com.msq.consumer;

import com.rabbitmq.client.*;

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

/**
 * Author: masiqi
 * Created on: 2022/10/04
 */
public class Consumer_PubSub2 {

    public static void main(String[] args) throws IOException, TimeoutException {
        ConnectionFactory factory = new ConnectionFactory();
        factory.setHost("localhost");
        factory.setVirtualHost("/msq");
        Connection connection = factory.newConnection();
        Channel channel = connection.createChannel();
//        channel.queueDeclare("fanout_queue1", true, false, false, null);  无需重复声明
        Consumer consumer = new DefaultConsumer(channel){
            @Override
            public void handleDelivery(String consumerTag,
                                       Envelope envelope,
                                       AMQP.BasicProperties properties,
                                       byte[] body){
                System.out.println("收到的消息:" + new String(body));
                System.out.println("消费者2将打印此信息到控制台...");
            }
        };
        channel.basicConsume("fanout_queue2", true, consumer);
    }
}

4. routing模式

1)生产者

package producer;

import com.rabbitmq.client.BuiltinExchangeType;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;

import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.concurrent.TimeoutException;

/**
 * Author: masiqi
 * Created on: 2022/10/04
 */
public class Producer_Routing {

    public static void main(String[] args) throws IOException, TimeoutException {
        // 1.创建工厂
        ConnectionFactory factory = new ConnectionFactory();

        // 2.设置参数
        factory.setHost("localhost");
        factory.setVirtualHost("/msq");

        // 3.创建connection
        Connection connection = factory.newConnection();

        // 4.创建channel
        Channel channel = connection.createChannel();
        /**
         * 1.exchange:交换机名称
         * 2.type:交换机类型
         *      - Direct:定向
         *      - Fanout:广播
         *      - Topic:通配符
         *      - Headers:参数匹配(用得少)
         * 3.durable:是否持久化
         * 4.autoDelete:是否自动删除
         * 5.internal:是否内部使用,一般赋值false
         * 6.arguments:参数
         */
        // 5.创建exchange
        String exchangeName = "test_direct";
        channel.exchangeDeclare(exchangeName, BuiltinExchangeType.DIRECT, true, false, false, null);

        // 6.创建queue
        channel.queueDeclare("direct_queue1", true, false, false, null);
        channel.queueDeclare("direct_queue2", true, false, false, null);

        // 7.绑定queue和exchange
        /**
         * 1.queue:队列名称
         * 2.exchange:交换机名称
         * 3.routingkey:路由键,绑定规则
         *      - exchange的type为FANOUT,则key为""
         *      - exchange的type为DIRECT,key则根据需要进行指定
         */
        channel.queueBind("direct_queue1", exchangeName, "error");  //error日志额外发往queue1
        channel.queueBind("direct_queue2", exchangeName, "info"); //info、warn、error日志发往queue2
        channel.queueBind("direct_queue2", exchangeName, "warn");
        channel.queueBind("direct_queue2", exchangeName, "error ");

        String message = "2022-10-04 msq在学习rabbitmq,发了一条warn消息";
        channel.basicPublish(exchangeName, "warn", null, message.getBytes(StandardCharsets.UTF_8));
        message = "2022-10-04 msq在学习rabbitmq,发了一条info消息";
        channel.basicPublish(exchangeName, "info", null, message.getBytes(StandardCharsets.UTF_8));

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

2)消费者

消费者1:

 package com.msq.consumer;

import com.rabbitmq.client.*;

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

/**
 * Author: masiqi
 * Created on: 2022/10/04
 */
public class Consumer_Direct1 {

    public static void main(String[] args) throws IOException, TimeoutException {
        ConnectionFactory factory = new ConnectionFactory();
        factory.setHost("localhost");
        factory.setVirtualHost("/msq");
        Connection connection = factory.newConnection();
        Channel channel = connection.createChannel();
//        channel.queueDeclare("fanout_queue1", true, false, false, null);  无需重复声明
        Consumer consumer = new DefaultConsumer(channel){
            @Override
            public void handleDelivery(String consumerTag,
                                       Envelope envelope,
                                       AMQP.BasicProperties properties,
                                       byte[] body){
                System.out.println("收到的消息:" + new String(body));
                System.out.println("消费者1将保存error信息到数据库...");
            }
        };
        channel.basicConsume("direct_queue1", true, consumer);
    }
}

消费者2

 package com.msq.consumer;

import com.rabbitmq.client.*;

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

 /**
  * Author: masiqi
  * Created on: 2022/10/04
  */
 public class Consumer_Direct2 {

     public static void main(String[] args) throws IOException, TimeoutException {
         ConnectionFactory factory = new ConnectionFactory();
         factory.setHost("localhost");
         factory.setVirtualHost("/msq");
         Connection connection = factory.newConnection();
         Channel channel = connection.createChannel();
 //        channel.queueDeclare("fanout_queue1", true, false, false, null);  无需重复声明
         Consumer consumer = new DefaultConsumer(channel){
             @Override
             public void handleDelivery(String consumerTag,
                                        Envelope envelope,
                                        AMQP.BasicProperties properties,
                                        byte[] body){
                 System.out.println("收到的消息:" + new String(body));
                 System.out.println("消费者2将打印日志信息到数据库...");
             }
         };
         channel.basicConsume("direct_queue2", true, consumer);
     }
 }

3)运行结果

image.png

image.png

5. topic模式

1)生产者

package producer;

import com.rabbitmq.client.BuiltinExchangeType;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;

import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.concurrent.TimeoutException;

/**
 * Author: masiqi
 * Created on: 2022/10/04
 */
public class Producer_Topic {

    public static void main(String[] args) throws IOException, TimeoutException {
        // 1.创建工厂
        ConnectionFactory factory = new ConnectionFactory();

        // 2.设置参数
        factory.setHost("localhost");
        factory.setVirtualHost("/msq");

        // 3.创建connection
        Connection connection = factory.newConnection();

        // 4.创建channel
        Channel channel = connection.createChannel();
        /**
         * 1.exchange:交换机名称
         * 2.type:交换机类型
         *      - Direct:定向
         *      - Fanout:广播
         *      - Topic:通配符
         *      - Headers:参数匹配(用得少)
         * 3.durable:是否持久化
         * 4.autoDelete:是否自动删除
         * 5.internal:是否内部使用,一般赋值false
         * 6.arguments:参数
         */
        // 5.创建exchange
        String exchangeName = "test_topic";
        channel.exchangeDeclare(exchangeName, BuiltinExchangeType.TOPIC, true, false, false, null);

        // 6.创建queue
        channel.queueDeclare("topic_queue1", true, false, false, null);
        channel.queueDeclare("topic_queue2", true, false, false, null);

        // 7.绑定queue和exchange
        /**
         * 1.queue:队列名称
         * 2.exchange:交换机名称
         * 3.routingkey:路由键,绑定规则
         *      - exchange的type为FANOUT,则key为""
         *      - exchange的type为DIRECT,key则根据需要进行指定
         *      - exchange的type为TOPIC,key为正则表达式
         */
        //  场景:1、所有error级别的日志存入数据库;2、sale模块的所有级别日志存入数据库;    ===》队列1
        //      3、所有日志打印到控制台    ==》队列2
        channel.queueBind("topic_queue1", exchangeName, "#.error");
        channel.queueBind("topic_queue1", exchangeName, "sale.*");
        channel.queueBind("topic_queue2", exchangeName, "*.*");

        String message = "2022-10-05  sale.info:销售了10件商品";
        channel.basicPublish(exchangeName, "sale.info", null, message.getBytes(StandardCharsets.UTF_8));
        message = "2022-10-05 sale.error:销售出错";
        channel.basicPublish(exchangeName, "sale.error ", null, message.getBytes(StandardCharsets.UTF_8));
        message = "2022-10-05 sale.warn:销售有风险";
        channel.basicPublish(exchangeName, "sale.warn ", null, message.getBytes(StandardCharsets.UTF_8));

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

2)消费者

消费者1:

 package com.msq.consumer;

import com.rabbitmq.client.*;

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

 /**
  * Author: masiqi
  * Created on: 2022/10/04
  */
 public class Consumer_Topic1 {

     public static void main(String[] args) throws IOException, TimeoutException {
         ConnectionFactory factory = new ConnectionFactory();
         factory.setHost("localhost");
         factory.setVirtualHost("/msq");
         Connection connection = factory.newConnection();
         Channel channel = connection.createChannel();
 //        channel.queueDeclare("fanout_queue1", true, false, false, null);  无需重复声明
         Consumer consumer = new DefaultConsumer(channel){
             @Override
             public void handleDelivery(String consumerTag,
                                        Envelope envelope,
                                        AMQP.BasicProperties properties,
                                        byte[] body){
                 String msg = new String(body);
                 System.out.println("收到的消息:" + msg);
                 if(msg.contains("error")){
                     System.out.println("消费者1将错误日志信息保存到数据库...");
                 }else if(msg.contains("sale")){
                     System.out.println("消费者1将sale日志信息保存到数据库...");
                 }
             }
         };
         channel.basicConsume("topic_queue1", true, consumer);
     }
 }

消费者2:

 package com.msq.consumer;

import com.rabbitmq.client.*;

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

 /**
  * Author: masiqi
  * Created on: 2022/10/04
  */
 public class Consumer_Topic2 {

     public static void main(String[] args) throws IOException, TimeoutException {
         ConnectionFactory factory = new ConnectionFactory();
         factory.setHost("localhost");
         factory.setVirtualHost("/msq");
         Connection connection = factory.newConnection();
         Channel channel = connection.createChannel();
 //        channel.queueDeclare("fanout_queue1", true, false, false, null);  无需重复声明
         Consumer consumer = new DefaultConsumer(channel){
             @Override
             public void handleDelivery(String consumerTag,
                                        Envelope envelope,
                                        AMQP.BasicProperties properties,
                                        byte[] body){
                 String msg = new String(body);
                 System.out.println("收到的消息:" + msg);
                 System.out.println("消费者2将打印所有日志到控制台...");
             }
         };
         channel.basicConsume("topic_queue2", true, consumer);
     }
 }