RabbitMQ系列(一)工作队列模式

引言

简单队列有个缺点,简单队列是一一对应的关系,即点对点,一个生产者对应一个消费者,按照这个逻辑,如果我们有一些比较耗时的任务,也就意味着需要大量的时间才能处理完毕,显然简单队列模式并不能满足我们的工作需求,我们今天再来看看工作队列。

工作队列模式

一个消息生产者,一个消息队列,多个消费者。同样也称为点对点模式。在这种模式中又可以分为两种模式,一种是两个消费中平均消费队列中的消息。也就说无论两个消费者的消费能力如何,都会平均获取消息。另一种方式则是,能者多劳模式,处理消息能力强的消费者会获取更多的消息,这种模式似乎更符合一些。
![在这里插入图片描述](https://img-blog.csdnimg.cn/20200421115401926.png
P:消息的生产者
C:消息的消费者
红色:队列

平均消费

消费者一
public class ReceiveOne {
    private static final String QUEUE = "work_queue_message";

    public static void main(String[] args) throws Exception {
        //获取链接
        Connection connection = ConnectionUtil.getConnection();
        //创建与Exchange的通道,每个连接可以创建多个通道,每个通道代表一个会话任务
        Channel channel = connection.createChannel();
        //声明队列,如果Rabbit中没有此队列将自动创建
        channel.queueDeclare(QUEUE, false, false, false, null);
        //同一时刻服务器只会发一条消息给消费者
        //channel.basicQos(1);
        // 监听队列
        Consumer consumer = new DefaultConsumer(channel){
            @Override
            public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
                try {
                    String message = new String(body,"utf-8");
                    System.out.println(" 消费者一消费消息 " + message);
                    //休眠
                    Thread.sleep(10);
                    //手动确认消息
                    //channel.basicAck(envelope.getDeliveryTag(), false);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        };
        //回调,true 消息自动确认,false 消息手动确认
        channel.basicConsume(QUEUE, true, consumer);

        //能者多劳模式将自动确认改为手动确认
        //channel.basicConsume(QUEUE, false, consumer);
    }
}
消费者二
public class ReceiveTwo {
    private static final String QUEUE = "work_queue_message";

    public static void main(String[] args) throws Exception {
        //获取链接
        Connection connection = ConnectionUtil.getConnection();
        //创建与Exchange的通道,每个连接可以创建多个通道,每个通道代表一个会话任务
        Channel channel = connection.createChannel();
        //声明队列,如果Rabbit中没有此队列将自动创建
        channel.queueDeclare(QUEUE, false, false, false, null);
        //同一时刻服务器只会发一条消息给消费者
        //channel.basicQos(1);
        // 监听队列
        Consumer consumer = new DefaultConsumer(channel) {
            @Override
            public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
                
                try {
                    String message = new String(body, "utf-8");
                    System.out.println(" 消费者二消费消息 " + message);
                    //休眠
                    Thread.sleep(1000);
                    //手动确认消息
                    //channel.basicAck(envelope.getDeliveryTag(), false);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        };
        //回调,true 消息自动确认,false 消息手动确认
        channel.basicConsume(QUEUE, true, consumer);

        //能者多劳模式将自动确认改为手动确认
//        channel.basicConsume(QUEUE, false, consumer);
    }
}
生产者
public class Producer {
    //队列名称
    private static final String QUEUE = "work_queue_message";

    public static void main(String[] args) throws Exception {
        //获取链接
        Connection connection = ConnectionUtil.getConnection();
        //创建与Exchange的通道,每个连接可以创建多个通道,每个通道代表一个会话任务
        Channel channel = connection.createChannel();
        //声明队列,如果Rabbit中没有此队列将自动创建
        channel.queueDeclare(QUEUE, false, false, false, null);
        for (int i = 1; i <= 100; i++) {
            String message = " 生产消息_" + i;
            //消息发布方法
            channel.basicPublish("", QUEUE, null, message.getBytes());
            System.out.println("生产者发送消息 :" + message );
        }

        //关闭资源
        channel.close();
        connection.close();
    }

}

测试结果
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
1、消费者1和消费者2获取到的消息内容是不同的,同一个消息只能被一个消费者获取。
2、消费者1和消费者2获取到的消息的数量是相同的,一个是消费奇数号消息,一个是偶数。

上面的代码我们可以看到,消费者1的处理能力比消费者2更强,但是当我们看到结果的时候会发现,两个消费者获得消息的数量却是一样的。这就是work模式中的平均消费模式。

能者多劳

由于上方模拟的是非常简单的消息队列的消费,假如有一些非常耗时的任务,某个消费者在缓慢地进行处理,而另一个消费者则空闲,显然是非常消耗资源的。

再举一个例子,一个1年的程序员,跟一个3年的程序员,分配相同的任务量,明显3年的程序员处理起来更加得心应手,很快就无所事事了,但是3年的程序员拿着非常高的薪资!显然3年的程序员应该承担更多的责任,那怎么办呢?

公平分发。

其实发生上述问题的原因是 RabbitMQ 收到消息后就立即分发出去,而没有确认各个工作者未返回确认的消息数量,类似于TCP/UDP中的UDP,面向无连接。

因此我们可以使用 basicQos 方法,并将参数 prefetchCount 设为1,告诉 RabbitMQ 我每次值处理一条消息,你要等我处理完了再分给我下一个。这样 RabbitMQ 就不会轮流分发了,而是寻找空闲的工作者进行分发。
打开上述代码的注释:

同一时刻服务器只会发一条消息给消费者

channel.basicQos(1);

开启这行 表示使用手动确认模式

channel.basicAck(envelope.getDeliveryTag(), false);

同时改为手动确认:

channel.basicConsume(QUEUE, false, consumer);

需要注意的 basicAck 方法需要传递两个参数
deliveryTag(唯一标识 ID):当一个消费者向 RabbitMQ 注册后,会建立起一个 Channel ,RabbitMQ 会用 basic.deliver 方法向消费者推送消息,这个方法携带了一个 delivery tag, 它代表了 RabbitMQ 向该 Channel 投递的这条消息的唯一标识 ID,是一个单调递增的正整数,delivery tag 的范围仅限于 Channel
multiple:为了减少网络流量,手动确认可以被批处理,当该参数为 true 时,则可以一次性确认 delivery_tag 小于等于传入值的所有消息。

测试结果
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
相关博文:
RabbitMQ系列(一)工作队列模式
RabbitMQ系列(二)确认模式
RabbitMQ系列(三)发布订阅模式
RabbitMQ系列(四)路由模式
RabbitMQ系列(五)主题模式

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值