RabbitMQ入门案例

RabbitMQ 是目前比较主流的MQ消息队列中间件,下面简单总结RabbitMQ入门时所做的一些笔记

1.RabbitMQ 入门案例

需求:用 Java 编写两个程序。发送单个消息的生产者和接收消息并打印出来的消费者

1.1 添加依赖

xml 复制代码
<!--rabbitmq 依赖客户端-->
<dependency>
    <groupId>com.rabbitmq</groupId>
    <artifactId>amqp-client</artifactId>
    <version>5.8.0</version>
</dependency>
<!--操作文件流的一个依赖-->
<dependency>
    <groupId>commons-io</groupId>
    <artifactId>commons-io</artifactId>
    <version>2.6</version>
</dependency>

1.2 消息生产者

创建一个类作为生产者,最终生产消息到 RabbitMQ 的队列里

步骤:

  1. 创建 RabbitMQ 连接工厂
  2. 进行 RabbitMQ 工厂配置信息
  3. 创建 RabbitMQ 连接
  4. 创建 RabbitMQ 信道
  5. 生成一个队列
  6. 发送一个消息到交换机,交换机发送到队列。"" 代表默认交换机
java 复制代码
/**
 * <p>Class: Producer </p>
 * <p>Description: 生产者:发消息</p>
 *
 * @author zhouyi
 * @version 1.0
 * @date 2023/7/9
 */
public class Producer {
    //对列名称
    public static final String QUEUE_NAME = "hello";

    //发消息
    public static void main(String[] args) throws IOException, TimeoutException {
        //创建一个连接工厂
        ConnectionFactory factory = new ConnectionFactory();
        //工厂IP 连接RabbitMQ对列
        factory.setHost("8.219.165.36");
        //用户名
        factory.setUsername("admin");
        //密码
        factory.setPassword("admin123");

        //创建连接
        Connection connection = factory.newConnection();
        //获取信道
        Channel channel = connection.createChannel();
        /**
         * 生产一个对列
         * 1.对列名称
         * 2.对列里面的消息是否持久化,默认情况下,消息存储在内存中
         * 3.该队列是否只供一个消费者进行消费,是否进行消息共享,true可以多个消费者消费 false:只能一个消费者消费
         * 4.是否自动删除,最后一个消费者端开链接以后,该队列是否自动删除,true表示自动删除
         * 5.其他参数
         */
        channel.queueDeclare(QUEUE_NAME, false, false, false, null);
        //发消息
        String message = "Hello,world";
        /**
         * 发送一个消息
         * 1.发送到哪个交换机
         * 2.路由的key值是哪个本次是队列的名称
         * 3.其他参数信息
         * 4.发送消息的消息体
         */
        channel.basicPublish("", QUEUE_NAME, null, message.getBytes());
        System.out.println("消息发送完毕");
    }
}

运行代码发现如下错误,即读写权限没设置好

bash 复制代码
Caused by: com.rabbitmq.client.ShutdownSignalException: connection error; protocol method: #method<connection.close>(reply-code=530, reply-text=NOT_ALLOWED - access to vhost '/' refused for user 'admin', class-id=10, method-id=40)

再次运行看到消息队列中已存在消息

方法解释

  • 声明队列:
java 复制代码
channel.queueDeclare(队列名/String, 持久化/boolean, 共享消费/boolean, 自动删除/boolean, 配置参数/Map);

配置参数现在是 null,后面死信队列、延迟队列等会用到,如:队列的优先级

队列里的消息如果没有被消费,何去何从?(死信队列)

java 复制代码
Map<String, Object> params = new HashMap();
// 设置队列的最大优先级 最大可以设置到 255 官网推荐 1-10 如果设置太高比较吃内存和 CPU
params.put("x-max-priority", 10);
// 声明当前队列绑定的死信交换机
params.put("x-dead-letter-exchange", Y_DEAD_LETTER_EXCHANGE);
// 声明当前队列的死信路由 key
params.put("x-dead-letter-routing-key", "YD");
channel.queueDeclare(QUEUE_NAME, true, false, false, params);
  • 发布消息:

    java 复制代码
    channel.basicPublish(交换机名/String, 队列名/String, 配置参数/Map, 消息/String);

    配置参数现在是 null,后面死信队列、延迟队列等会用到,如:发布的消息优先级

    发布的消息标识符 id

java 复制代码
// 给消息赋予 优先级 ID 属性
AMQP.BasicProperties properties = new AMQP.BasicProperties().builder().priority(10).messageId("1")build();
channel.basicPublish("", QUEUE_NAME, properties, message.getBytes());

1.3 消息消费者

创建一个类作为消费者,消费 RabbitMQ 队列的消息,消息消费是通过Channel来完成的

java 复制代码
public class Consumer {

    //队列的名称
    public static final String QUEUE_NAME = "hello";

    //接受消息
    public static void main(String[] args) throws IOException, TimeoutException {
        //创建一个连接工厂
        ConnectionFactory factory = new ConnectionFactory();
        //工厂IP 连接RabbitMQ对列
        factory.setHost("8.219.165.36");
        //用户名
        factory.setUsername("admin");
        //密码
        factory.setPassword("admin123");
        Connection connection = factory.newConnection();

        Channel channel = connection.createChannel();

        //声明接收消息
        DeliverCallback deliverCallback = (consumerTag, message) -> {
            System.out.println(new String(message.getBody()));
        };
        //取消消息时的回调
        CancelCallback cancelCallback = consumerTag -> {
            System.out.println("消息消费被中断");
        };

        /**
         * 消费者消费消息
         * 1.消费哪个队列
         * 2.消费成功之后是否要自动应答true:代表自动应答false:代表手动应答
         * 3.消费者未成功消费的回调
         * 4.消费者取消消费的回调
         */
        channel.basicConsume(QUEUE_NAME, true, deliverCallback, cancelCallback);
    }
}

运行结果如下

此时队列里面的消息都被全部消费了

说明消息已被消费掉了

2.Work Queues(工作队列)

Work Queues 是工作队列(又称任务队列)的主要思想是避免立即执行资源密集型任务,而不得不等待它完成。相反我们安排任务在之后执行。我们把任务封装为消息并将其发送到队列。在后台运行的工作进程将弹出任务并最终执行作业。当有多个工作线程时,这些工作线程将一起处理这些任务。

2.1 轮询消费

轮询消费消息指的是轮流消费消息,即每个工作队列都会获取一个消息进行消费,并且获取的次数按照顺序依次往下轮流。

案例中生产者叫做 Task,一个消费者就是一个工作队列,启动两个工作队列消费消息,这个两个工作队列会以轮询的方式消费消息。

轮询案例

  • 首先把 RabbitMQ 的配置参数封装为一个工具类:`RabbitMQUtils,创建信道的工具类
java 复制代码
public class RabbitMqUtils {
    //得到一个连接的 channel
    public static Channel getChannel() throws Exception{
        //创建一个连接工厂
        ConnectionFactory factory = new ConnectionFactory();
        factory.setHost("182.92.234.71");
        factory.setUsername("admin");
        factory.setPassword("123");
        Connection connection = factory.newConnection();
        Channel channel = connection.createChannel();
        return channel;
    }
}

创建一个工作线程,相当于一个消费者

java 复制代码
public class Work01 {

    //队列的名称
    public static final String QUEUE_NAME = "hello";

    //接收消息
    public static void main(String[] args) throws IOException, TimeoutException {
        Channel channel = RabbitMQUtils.getChannel();
        //消息的接受
        DeliverCallback deliverCallback = (consumerTag, message) -> {
            System.out.println("接收到的消息:" + new String(message.getBody()));
        };

        //消息接受被取消时,执行下面的内容
        CancelCallback cancelCallback = consumerTag -> {
            System.out.println(consumerTag + "消息被消费者取消消费接口回调逻辑");
        };

        //消息的接受
        channel.basicConsume(QUEUE_NAME, true, deliverCallback, cancelCallback);
    }
}

为了演示多个工作线程,可以在IDEA中设置允许同时运行多次

创建好一个工作队列,只需要以多线程方式启动两次该 main 函数即可,以 first、second 区别消息队列。

要开启多线程功能,首先启动该消息队列,然后如图开启多线程:

先运行第一个工作线程:

在运行第二个工作线程,此时已经开了两个工作线程,如下

  • 创建一个生产者,发送消息进程,为了方便消息直接从控制台输入

    java 复制代码
    public class Task01 {
        
        //队列名称
        public static final String QUEUE_NAME="hello";
    
        //发送大量消息
        public static void main(String[] args) throws IOException, TimeoutException {
    
            Channel channel = RabbitMQUtils.getChannel();
            //队列的声明
            channel.queueDeclare(QUEUE_NAME,false,false,false,null);
    
            //发送消息
            //从控制台当中接受信息
            Scanner scanner = new Scanner(System.in);
            while (scanner.hasNext()) {
                String message = scanner.next();
                channel.basicPublish("",QUEUE_NAME,null,message.getBytes());
                System.out.println("消息发送完成:"+message);
            }
        }
    }

结果验证

假设生产者生产 AA BB CC DD这四条消息,理论上工作线程C1和工作线程C2轮询接收到消息,期望测试结果如下

  • 生产者(Task01) 生产AA消息,工作线程C1接收到AA消息;
  • 生产者(Task01) 生产BB消息,工作线程C2接收到BB消息;
  • 生产者(Task01) 生产CC消息,工作线程C1接收到CC消息;
  • 生产者(Task01) 生产DD消息,工作线程C2接收到DD消息;

实际运行结果如下,和期望一致

相关推荐
大新新大浩浩2 小时前
arm64适配系列文章-第六章-arm64环境上rabbitmq-management的部署,构建cluster-operator
rabbitmq·arm
躺不平的理查德4 小时前
General Spark Operations(Spark 基础操作)
大数据·分布式·spark
talle20214 小时前
Zeppelin在spark环境导出dataframe
大数据·分布式·spark
渣渣盟4 小时前
大数据开发环境的安装,配置(Hadoop)
大数据·hadoop·分布式
Angindem5 小时前
SpringClound 微服务分布式Nacos学习笔记
分布式·学习·微服务
电脑玩家粉色男孩8 小时前
2、Ubuntu 环境下安装RabbitMQ
linux·rabbitmq
龙仔72513 小时前
离线安装rabbitmq全流程
分布式·rabbitmq·ruby
〆、风神16 小时前
Spring Boot 整合 Lock4j + Redisson 实现分布式锁实战
spring boot·分布式·后端
胡萝卜糊了Ohh17 小时前
kafka
分布式·kafka