RabbitMQ消息队列的五种模式-工作模式

前言

第一个教程中我们编写了程序来发送和接收来自命名队列的消息。在这一部分中,我们将创建一个工作队列,该队列将用于在多个工作人员之间分配耗时的任务。

工作队列(又称任务队列)的主要思想是避免立即执行资源密集型任务,而不得不等待它完成。相反,我们安排任务在以后完成。我们将任务封装 为消息并将其发送到队列。在后台运行的工作进程将弹出任务并最终执行作业。当您运行许多工作人员时,任务将在他们之间共享。

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

循环调度

使用任务队列的优点之一是能够轻松并行化工作。如果我们正在积压工作,我们可以增加更多的工人,这样就可以轻松扩展。

首先,让我们尝试同时运行两个工作程序实例。他们俩都将从队列中获取消息。默认情况下,RabbitMQ将每个消息依次发送给下一个使用者。平均而言,每个消费者都会收到相同数量的消息。这种分发消息的方式称为循环。

消息确认

执行任务可能需要几秒钟。您可能想知道,如果其中一个使用者开始一项漫长的任务而仅部分完成而死掉,会发生什么。使用我们当前的代码,RabbitMQ一旦向消费者发送了一条消息,便立即将其标记为删除。在这种情况下,如果您杀死一个工人,我们将丢失正在处理的消息。我们还将丢失所有发送给该特定工作人员但尚未处理的消息。

但是我们不想丢失任何任务。如果一个工人死亡,我们希望将任务交付给另一个工人。

为了确保消息永不丢失,RabbitMQ支持 消息确认。消费者发送回一个确认(告知),告知RabbitMQ特定的消息已被接收,处理,并且RabbitMQ可以自由删除它。

如果使用者死了(其通道已关闭,连接已关闭或TCP连接丢失)而没有发送确认,RabbitMQ将了解消息未完全处理,并将重新排队。如果同时有其他消费者在线,它将很快将其重新分发给另一个消费者。这样,您可以确保即使工人偶尔死亡也不会丢失任何消息。

消息持久性

我们已经学会了如何确保即使消费者死亡,任务也不会丢失。但是,如果RabbitMQ服务器停止,我们的任务仍然会丢失。

RabbitMQ退出或崩溃时,它将忘记队列和消息,除非您告知不要这样做。要确保消息不会丢失,需要做两件事:我们需要将队列和消息都标记为持久。

首先,我们需要确保RabbitMQ永远不会丢失我们的队列。为此,我们需要将其声明为持久的

// 1.name 队列名称 
// 2.durable 是否持久化消息(true/false)
// 3.autoDelete 是否自动删除队列
// 4.exclusive 是否排外的(具体百度)
// 5.noWait 是否等待服务器返回
channel.QueueDeclare(WORK_QUEUE_NAME, true, false, false, null)

公平分配

例如,在有两名工人的情况下,当所有奇怪的消息都很重,甚至消息很轻时,一位工人将一直忙碌而另一位工人将几乎不做任何工作。好吧,RabbitMQ对此一无所知,并且仍将平均分配消息。

发生这种情况是因为RabbitMQ在消息进入队列时才调度消息。它不会查看使用者的未确认消息数。它只是盲目地将每第n条消息发送给第n个使用者。

为了克服这一点,我们可以将basicQos方法与prefetchCount=1一起使用,告诉RabbitMQ一次不要给工人一个以上的消息。

int prefetchCount = 1 ;
channel.basicQos(prefetchCount);

一、引入RabbitMQ开发包

<dependency>
	<groupId>com.rabbitmq</groupId>
	<artifactId>amqp-client</artifactId>
	<version>3.6.6</version>
</dependency>

二、连接工具

package com.example.demo.util;

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

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

/**
 * 消息队列连接工具
 *
 */
public class MQConnectionUtils {

    public static Connection connection() throws IOException, TimeoutException{
        ConnectionFactory factory = new ConnectionFactory();
        factory.setHost("127.0.0.1");
        factory.setUsername("guest");
        factory.setPassword("guest");
        factory.setPort(5672);

        return factory.newConnection();
    }
}

三、生产者

package com.example.demo.produce;

import com.example.demo.util.MQConnectionUtils;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;

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

/**
 * 工作模式 - 生产者
 *
 */
public class WorkProduce {

    private static final String WORK_QUEUE_NAME = "work_queue_name";

    public static void main(String[] args) throws IOException, TimeoutException {
        Connection connection = MQConnectionUtils.connection();
        Channel channel = connection.createChannel();

        channel.queueDeclare(WORK_QUEUE_NAME, true,false,false,null);
        channel.basicQos(1);
        for (int i = 0; i < 20; i++){
            String msg = "hello, 我是工作模式" + (i + 1);
            channel.basicPublish("",Constant.WorkPattern.QUEUE_NAME,null, msg.getBytes());
        }
        channel.close();
        connection.close();

    }
}

四、消费者

package com.example.demo.consume;

import com.example.demo.util.MQConnectionUtils;
import com.rabbitmq.client.*;

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

/**
 * 工作模式- 消费者1
 */
public class WorkConsume1 {
    
    private static final String WORK_QUEUE_NAME = "work_queue_name";

    public static void main(String[] args) throws IOException, TimeoutException {
        Connection connection = MQConnectionUtils.connection();
        Channel channel = connection.createChannel();
        
        // 声明一个队列
        channel.queueDeclare(WORK_QUEUE_NAME, false,false,false,null);
        channel.basicQos(1);

        DefaultConsumer consumer = new DefaultConsumer(channel){
            @Override
            public void handleDelivery(String consumeTag, Envelope envelope, AMQP.BasicProperties basicProperties, byte[] body)
                throws IOException{
                try {
                    String msg = new String(body,"utf-8");
                    System.out.println("消费者收到的消息为:" + msg);
                    Thread.sleep(1000);
                }catch (Exception r){
                    r.printStackTrace();
                }finally {
                    // 手动确认
                    channel.basicAck(envelope.getDeliveryTag(), false);
                }
            }
        };
        channel.basicConsume(WORK_QUEUE_NAME, false, consumer);
    }
}
package com.example.demo.consume;

import com.example.demo.util.MQConnectionUtils;
import com.rabbitmq.client.*;

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

/**
 * 消息队列 - 消费者2
 */
public class WorkConsume2 {
    
    private static final String WORK_QUEUE_NAME = "work_queue_name";

    public static void main(String[] args) throws IOException, TimeoutException {
        Connection connection = MQConnectionUtils.connection();
        Channel channel = connection.createChannel();
        
        // 声明一个队列
        channel.queueDeclare(WORK_QUEUE_NAME, false,false,false,null);
        channel.basicQos(1);

        DefaultConsumer consumer = new DefaultConsumer(channel){
            @Override
            public void handleDelivery(String consumeTag, Envelope envelope, AMQP.BasicProperties basicProperties, byte[] body)
                    throws IOException{
                try {
                    String msg = new String(body,"utf-8");
                    System.out.println("工作模式消费者收到的消息为:" + msg);
                    Thread.sleep(1000);
                }catch (Exception r){
                    r.printStackTrace();
                }finally {
                    // 手动确认
                    channel.basicAck(envelope.getDeliveryTag(), false);
                }
            }
        };
        channel.basicConsume(WORK_QUEUE_NAME, false, consumer);
    }
}

五、启动服务

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值