RabbitMQ系列(四)RabbitMQ进阶-Queue队列特性 (二)工作队列 Work模式

work 工作队列(工作模式)

工作模式就是work模式,1:n 指1个生产者 多个消费者,消费者存在竞争关系,只有一个消费者会获得消息,进行消费,多个消费者竞争消费
比较适用于生产环境->负载均衡,能者多劳模式,如果机器网络较好,处理速度较快,那么采用这种方式,该机器消费消息就较多,可以通过basicQos来调整策略
在这里插入图片描述
工作模式上代码
为了区分效率,我们新建2个消费者,设置不同的消费延迟时间,另外把消息确认机制设置为手动确认

2.1 生产者

依旧是Maven项目,项目 pom还是参考上一篇 新建maven工程,rabbitMQ简单队列

package com.jzj.mq.second;

import com.jzj.mq.conn.MqConnectUtil;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;

/**
 * 当前描述:
 *
 * @author: jiazijie
 * @since: 2020/6/17 下午10:31
 */
public class SecondProducer {
    private final static String QUEUE_NAME = "queue_test";

    public static void main(String[] argv) throws Exception {
        // 获取到连接以及mq通道
        Connection connection = MqConnectUtil.getConnectionDefault();
        // 从连接中创建通道
        Channel channel = connection.createChannel();

        /* 声明(创建)队列  queueDeclare( String queue, boolean durable, boolean exclusive, boolean autoDelete,  Map<String, Object> arguments)
         * queue - 队列名
         * durable - 是否是持久化队列, 队列的声明默认是存放到内存中的,如果rabbitmq重启会丢失
         * exclusie - 是否排外的,仅限于当前队列使用
         * autoDelete - 是否自动删除队列,当最后一个消费者断开连接之后队列是否自动被删除,可以通过界面 查看某个队列的消费者数量,当consumers = 0时队列就会自动删除
         * arguments - 队列携带的参数 比如 ttl-生命周期,x-dead-letter 死信队列等等
         */
        channel.queueDeclare(QUEUE_NAME, false, false, false, null);

        //定义消息内容(发布多条消息)
        for (int i = 0; i < 10; i++) {
            String message = "hello message " + i;
            /* 发送消息 String exchange, String routingKey, BasicProperties props, byte[] body
             * exchange - 交换机 ,"" 空时候指定的是 获取的virtualHost 虚拟服务器的 默认的exchang,每个virtualHost都有一个AMQP default type:direct 直接转发
             * queuename - 队列信息
             * props - 参数信息
             * message 消息体 byte[]类型
             */
            channel.basicPublish("", QUEUE_NAME, null, message.getBytes());

            System.out.println(" **** Producer  Sent Message: '" + message + "'");

            //模拟发送消息延时,便于演示多个消费者竞争接受消息
            Thread.sleep(i * 10);
        }

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

2.2 消费者1和消费者2

消费者1

package com.jzj.mq.second;

import com.jzj.mq.conn.MqConnectUtil;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.QueueingConsumer;

/**
 * 当前描述:
 *
 * @author: jiazijie
 * @since: 2020/6/17 下午10:32
 */
public class SecondConsumer1 {
    private final static String QUEUE_NAME = "queue_test";

    public static void main(String[] argv) throws Exception {
        Connection connection = MqConnectUtil.getConnectionDefault();
        Channel channel = connection.createChannel();

        /*确保这里的队列是存在的*/
        channel.queueDeclare(QUEUE_NAME, false, false, false, null);

        //!!!!! 同一时刻服务器只会发送一条消息给消费者
//        channel.basicQos(1);

        System.out.println(" **** Consumer->1 Waiting for messages. To exit press CTRL+C");


        QueueingConsumer consumer = new QueueingConsumer(channel);

        /* 消息确认机制
         * autoAck true:表示自动确认,只要消息从队列中获取,无论消费者获取到消息后是否成功消费,都会认为消息已经成功消费
         * autoAck false:表示手动确认,消费者获取消息后,服务器会将该消息标记为不可用状态,等待消费者的反馈,如果消费者一直没有反馈,那么该消息将一直处于不可用状态
         *          并且服务器会认为该消费者已经挂掉,不会再给其发送消息,直到该消费者反馈
         *          !!!!!! 注意这里是 false,手动确认
         */
        channel.basicConsume(QUEUE_NAME, false, consumer);

        while (true) {
            QueueingConsumer.Delivery delivery = consumer.nextDelivery();
            String message = new String(delivery.getBody());
            System.out.println(" **** Consumer->1 Received '" + message + "'");
            //消费者1 接收一条消息后休眠 100 毫秒,模仿复杂逻辑
             Thread.sleep(100);
            //返回确认状态
            channel.basicAck(delivery.getEnvelope().getDeliveryTag(),false);
        }
    }
}

消费者2

package com.jzj.mq.second;

import com.jzj.mq.conn.MqConnectUtil;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.QueueingConsumer;

/**
 * 当前描述:
 *
 * @author: jiazijie
 * @since: 2020/6/17 下午10:32
 */
public class SecondConsumer2 {
    private final static String QUEUE_NAME = "queue_test";

    public static void main(String[] argv) throws Exception {
        Connection connection = MqConnectUtil.getConnectionDefault();
        Channel channel = connection.createChannel();

        /*确保这里的队列是存在的*/
        channel.queueDeclare(QUEUE_NAME, false, false, false, null);

        //!!!!! 同一时刻服务器只会发送一条消息给消费者
//        channel.basicQos(1);
        
        System.out.println(" **** Consumer->2 Waiting for messages. To exit press CTRL+C");


        QueueingConsumer consumer = new QueueingConsumer(channel);

        /* 消息确认机制
         * autoAck true:表示自动确认,只要消息从队列中获取,无论消费者获取到消息后是否成功消费,都会认为消息已经成功消费
         * autoAck false:表示手动确认,消费者获取消息后,服务器会将该消息标记为不可用状态,等待消费者的反馈,如果消费者一直没有反馈,那么该消息将一直处于不可用状态
         *          并且服务器会认为该消费者已经挂掉,不会再给其发送消息,直到该消费者反馈
         *          !!!!!! 注意这里是 false,手动确认
         */
        channel.basicConsume(QUEUE_NAME, false, consumer);

        while (true) {
            QueueingConsumer.Delivery delivery = consumer.nextDelivery();
            String message = new String(delivery.getBody());
            System.out.println(" **** Consumer->2 Received '" + message + "'");
            //消费者1 接收一条消息后休眠 2000 毫秒,模仿复杂逻辑
             Thread.sleep(2000);
            //返回确认状态
            channel.basicAck(delivery.getEnvelope().getDeliveryTag(),false);
        }
    }
}
2.3 执行结果

先开启消费者1,在开启消费者2,然后再开启生产者,生产消息
在这里插入图片描述
执行结果,可以看到,1条消息只能被1个消费者消费,不管消费耗时多久,都是平均消费?这就有点怪了,我牛逼,我还不能多干点?很奇怪啊
!!!划重点,mq消息的发送与消费与 basicQos有关,
在这里插入图片描述

2.4 basicQos 是什么?

修改QOS后,查看执行结果
RabbitMQ提供了一种qos(服务质量保证)功能,即在非自动确认消息的前提下,如果一定数目的消息(通过基于consume或者channel设置Qos的值)未被确认前,不进行消费新的消息

basicQos 假如队列中现在有100条消息,现在我们是手动确认消息机制,如果消费者消费能力差,那么mq服务器会一次性把所有消息都推送过来,我们一台消费者客户端,1分钟同时要接收到300条消息,已经超过我们最大的负载,这时就可能导致,服务器资源被耗尽,消费者客户端卡死等情况,basicQos就有作用了,用于流控

basicQos(10) 在手动确认的前提下,如果有10条消息没有ack 应答,那么我们就暂时不推送消息,如果有1条应答了,我们就推送1条(基本原理是这,中间还涉及1个消费者Prefetch count- 后面介绍作用),保证系统稳定性,防止大量数据过来导致服务器挂掉

!!!现在我们把代码中 basicQos 注释放开,channel.basicQos(1); 再次测试一下功能
在这里插入图片描述
可以看到 能者多劳

消费者->1 休眠100ms,处理任务能力强,然后接收到了更多的MQ消息去消费
消费者->2 休眠2000ms,处理能力弱,只处理了个别消息
basicQos 等待消费者->1 处理完1条消息,恢复确认机制,当前消息已经被消费了,你可以下发下一条消息了,然后下发另一条,消费者1 再次接收到消息,再次消费,能者多劳

猜你喜欢

转载自blog.csdn.net/u010134642/article/details/106824679