RabbitMQ---work消息模型

1、work消息模型

工作队列或者竞争消费者模式

在第一篇教程中,我们编写了一个程序,从一个命名队列中发送并接受消息。在这里,我们将创建一个工作队列,在多个工作者之间分配耗时任务。

工作队列,又称任务队列。主要思想就是避免执行资源密集型任务时,必须等待它执行完成。相反我们稍后完成任务,我们将任务封装为消息并将其发送到队列。 在后台运行的工作进程将获取任务并最终执行作业。当你运行许多消费者时,任务将在他们之间共享,但是一个消息只能被一个消费者获取。

这个概念在Web应用程序中特别有用,因为在短的HTTP请求窗口中无法处理复杂的任务。

接下来我们来模拟这个流程:

o P:生产者:任务的发布者

o C1:消费者,领取任务并且完成任务,假设完成速度较快

o C2:消费者2:领取任务并完成任务,假设完成速度慢

面试题:避免消息堆积?

1)采用workqueue,多个消费者监听同一队列。

2)接收到消息以后,而是通过线程池,异步消费。

1.1、生产者

生产者与案例1中的几乎一样:

java 复制代码
public class Send {
   private final static String QUEUE_NAME = "test_work_queue";
   public static void main(String[] argv) throws Exception {
       // 获取到连接
       Connection connection = ConnectionUtil.getConnection();
       // 获取通道
       Channel channel = connection.createChannel();
       // 声明队列
       channel.queueDeclare(QUEUE_NAME, false, false, false, null);
       // 循环发布任务
       for (int i = 0; i < 50; i++) {
           // 消息内容
           String message = "task .. " + i;
           channel.basicPublish("", QUEUE_NAME, null, message.getBytes());
           System.out.println(" [x] Sent '" + message + "'");
       }
       // 关闭通道和连接
       channel.close();
       connection.close();
   }
}

不过这里我们是循环发送50条消息。

1.2、消费者1

java 复制代码
// 消费者1
public class Recv {
    private final static String QUEUE_NAME = "test_work_queue";
    public static void main(String[] argv) throws Exception {
        // 获取到连接
        Connection connection = ConnectionUtil.getConnection();
        // 获取通道
        final Channel channel = connection.createChannel();
        // 声明队列
        channel.queueDeclare(QUEUE_NAME, false, false, false, null);
        // 定义队列的消费者
        DefaultConsumer consumer = new DefaultConsumer(channel) {
            // 获取消息,并且处理,这个方法类似事件监听,如果有消息的时候,会被自动调用
            @Override
            public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties,
                                       byte[] body) throws IOException {
                // body 即消息体
                String msg = new String(body);
                System.out.println(" [消费者1] received : " + msg + "!");
                try {
                    // 模拟完成任务的耗时:1000ms
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                }
                // 手动ACK
                channel.basicAck(envelope.getDeliveryTag(), false);
            }
        };
        // 监听队列。
        channel.basicConsume(QUEUE_NAME, false, consumer);
    }
}

1.3、消费者2

java 复制代码
//消费者2
public class Recv2 {
    private final static String QUEUE_NAME = "test_work_queue";
    public static void main(String[] argv) throws Exception {
        // 获取到连接
        Connection connection = ConnectionUtil.getConnection();
        // 获取通道
        final Channel channel = connection.createChannel();
        // 声明队列
        channel.queueDeclare(QUEUE_NAME, false, false, false, null);
        // 定义队列的消费者
        DefaultConsumer consumer = new DefaultConsumer(channel) {
            // 获取消息,并且处理,这个方法类似事件监听,如果有消息的时候,会被自动调用
            @Override
            public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties,
                                       byte[] body) throws IOException {
                // body 即消息体
                String msg = new String(body);
                System.out.println(" [消费者2] received : " + msg + "!");
                try {
                    // 模拟完成任务的耗时:200ms
                    Thread.sleep(200);
                } catch (InterruptedException e) {
                }
                // 手动ACK
                channel.basicAck(envelope.getDeliveryTag(), false);
            }
        };
        // 监听队列。
        channel.basicConsume(QUEUE_NAME, false, consumer);
    }
}

与消费者1基本类似,就是没有设置消费耗时时间。

这里是模拟有些消费者快,有些比较慢。

接下来,两个消费者一同启动,然后发送50条消息:

可以发现,两个消费者各自消费了25条消息,而且各不相同,这就实现了任务的分发。

1.4、能者多劳

• 刚才的实现有问题吗?

o 消费者1比消费者2的效率要低,一次任务的耗时较长

o 然而两人最终消费的消息数量是一样的

o 消费者2大量时间处于空闲状态,消费者1一直忙碌

• 现在的状态属于是把任务平均分配,正确的做法应该是消费越快的人,消费的越多。

• 怎么实现呢?

o 我们可以使用basicQos方法和prefetchCount = 1设置。

o 这告诉RabbitMQ一次不要向工作人员发送多于一条消息。

o 或者换句话说,不要向工作人员发送新消息,直到它处理并确认了前一个消息。

o 相反,它会将其分派给不是仍然忙碌的下一个工作人员。

再次测试:

  ![在这里插入图片描述](https://img-blog.csdnimg.cn/a25bdfcd50bf41f9a6e51076560cd15f.png)
相关推荐
海里真的有鱼1 小时前
Spring Boot 项目中整合 RabbitMQ,使用死信队列(Dead Letter Exchange, DLX)实现延迟队列功能
开发语言·后端·rabbitmq
河南宽信李工1503806 16862 小时前
测绘航空摄影专项资质在洛阳市的获取流程
服务器
学习向前冲2 小时前
AD域控服务器
运维·服务器
青柠视频云2 小时前
青柠视频云——视频丢包(卡顿、花屏、绿屏)排查
服务器·网络·音视频
hgdlip2 小时前
查看ip地址的方法有几种?探索多样方法
运维·服务器·ip地址
danplus3 小时前
node发送邮件:如何实现Node.js发信功能?
服务器·node.js·外贸开发信·邮件群发·蜂邮edm邮件营销·邮件接口·营销邮件
BeyondESH3 小时前
Linux线程同步—竞态条件和互斥锁(C语言)
linux·服务器·c++
wn5313 小时前
【Go - 类型断言】
服务器·开发语言·后端·golang
hanniuniu133 小时前
详细解读,F5服务器负载均衡的技术优势
运维·服务器·负载均衡
PatrickYao04224 小时前
记一次安装discuz时遇到的错误
服务器