RaabitMq——工作队列工作模式

86 阅读5分钟

在默认路由下,当有多个工作线程时,可以共同完成这个工作队列,他们的模式有两种:轮训分发和不公平分发。

**1.轮训分发:依次按照顺序一条一条分发到每个工作线程处理,一个工作线处理完,下一个工作线程才能进行。 ** 案例:一个生产者发消息,两个消费者消费消息,采用轮训分发模式

(1)开启linux上rabbitmq的服务和关闭linux防火墙

/sbin/service rabbitmq-server start

systemctl stop firewalld

(2)写一个连接工厂,创建信道的工具类(减少代码重复)

①获取连接工厂

②设置连接rabbitmq的ip地址(即linux上的本地地址)

③设置用户名和密码

④获取连接,并创建信道

//连接工厂,创建信道工具类
public class RabbitUtils {
    // 得到一个连接的 channel
    public static Channel getChannel() throws Exception {
        // 创建一个连接工厂
        ConnectionFactory factory = new ConnectionFactory();
        factory.setHost("192.168.23.111");
        factory.setUsername("user");
        factory.setPassword("123");
        Connection connection = factory.newConnection();
        Channel channel = connection.createChannel();
        return channel;
    }
}

(3)写生产者发送消息

   ①调用工具类获取信道

   ②调用queueDeclare(队列名称,消息是否持久化,是否只供一个消费者消费,是否自动删除消息,其他参数)声明队列

   ③调用scanner从控制台输入信息发送

   ④调用basicPublic(路由,队列名称,其他参数,发送消息)方法发送消息
   
public class Task01 {
 
    //队列
    public static final  String QUEUE_NAME="hello";
 
    //发送消息
    public static void main(String[] args) throws Exception {
 
        //获取信道
        Channel channel = RabbitUtils.getChannel();
 
        //声明队列
        channel.queueDeclare(QUEUE_NAME,false,false,false,null);
 
        //从控制台发送消息
        Scanner sc=new Scanner(System.in);
        while (sc.hasNext()){
            String message = sc.nextLine();
            channel.basicPublish("",QUEUE_NAME,null,message.getBytes());
            System.out.println("发送消息完成:"+message);
        }
 
 
    }
 
}

(4)写两个消费者消费消息

    ①调用工具类获取信道

    ②调用接收消息回调函数deliverCallback(消息标记,消息),打印收到消息到控制台

    ③调用消息中断回调函数

    ④调用basicConsume(队列名称,是否自动应答,接收消息回调函数,消息中断回调函数)方法接收消息
    
//工作线程,消费者
public class Worker01 {
 
    //队列
    public static final  String QUEUE_NAME="hello";
 
    //接受消息
    public static void main(String[] args) throws Exception {
        //获取信道
        Channel channel = RabbitUtils.getChannel();
 
        //声明,接受消息
        DeliverCallback deliverCallback=(consumerTag, message)->{
            System.out.println("接收到的消息:"+new String(message.getBody()));
        };
 
        //消息被中断声明
        CancelCallback cancelCallback=(consumerTag)->{
            System.out.println(consumerTag+"消息消费被中断");
        };
 
        System.out.println("C1等待接受消息......");
        //消息接收
        channel.basicConsume(QUEUE_NAME,true,deliverCallback,cancelCallback);
 
    }
 
}
//工作线程,消费者

public class Worker02 {
 
    //队列
    public static final  String QUEUE_NAME="hello";
 
    //接受消息
    public static void main(String[] args) throws Exception {
        //获取信道
        Channel channel = RabbitUtils.getChannel();
 
        //声明,接受消息
        DeliverCallback deliverCallback=(consumerTag, message)->{
            System.out.println("接收到的消息:"+new String(message.getBody()));
        };
 
        //消息被中断声明
        CancelCallback cancelCallback=(consumerTag)->{
            System.out.println(consumerTag+"消息消费被中断");
        };
 
        System.out.println("C2等待接受消息......");
        //消息接收
        channel.basicConsume(QUEUE_NAME,true,deliverCallback,cancelCallback);
 
    }
 
}

(5)测试,启动生产者和两个消费者

微信截图_20220310113007.png

微信截图_20220310113015.png

微信截图_20220310113022.png

可以看出C1和C2为分别依次接收生产者消息

2、不公平分发:多个线程工作下,可以为每个线程分配固定数量的消息接收,两个线程可以同时处理接收消息

案例:一个生产者发送5条消息,一个处理快的(睡眠1秒)消费者分发3条,一个处理慢的消费者(睡眠30秒)分发两条

(1)启动服务,关闭防火墙,创建工具类同上一样

(2)创建线程睡眠工具类,用于模拟消费者处理速度

//线程等待工具类
public class SleepUtils {
 
    public static void sleep(int second){
        try {
            Thread.sleep(1000*second);
        } catch (InterruptedException _ignored) {
            Thread.currentThread().interrupt();
        }
    }
}

(3)创建生产者发送消息

    ①获取信道,开启信道发布确认(手动应答消息不会丢失)

    ②声明队列,开启消息持久化(防止服务器宕机消息的丢失)

    ③控制台输入发送消息

    ④发送消息,消息持久化保存到磁盘上
public class Task02 {
 
    //队列
    public static final  String TASK_NAME="ack_queue";
 
    public static void main(String[] args) throws Exception {
        Channel channel = RabbitUtils.getChannel();
 
        //开启信道发布确认
        channel.confirmSelect();
 
        //声明队列
        boolean durable=true;//需要队列持久化
        channel.queueDeclare(TASK_NAME,durable,false,false,null);
 
        //控制台输入信息
        Scanner sc=new Scanner(System.in);
        while (sc.hasNext()){
            String message = sc.nextLine();
 
            //第三个参数设置消息持久化,保存到磁盘上
            channel.basicPublish("",TASK_NAME, MessageProperties.PERSISTENT_TEXT_PLAIN
                    ,message.getBytes("UTF-8"));
            System.out.println("生产者发出消息:"+message);
        }
 
 
    }
    
}

(4)创建处理快的消费者接收消息

    ①获取信道连接

    ②消息接收回调函数,调用睡眠工具类,睡眠1秒,调用basicAck(消息标记,是否批量应答)方法自动应答

    ③消息中断回调函数调用

    ④调用basicQos(分发数量)方法设置不公平分发,设置数量为3

    ⑤接收消息
public class Worker03 {
 
    //队列
    public static final  String TASK_NAME="ack_queue";
 
    public static void main(String[] args) throws Exception {
 
        Channel channel = RabbitUtils.getChannel();
 
        System.out.println("C1等待接收处理,时间较短......");
 
        //声明,接受消息
        DeliverCallback deliverCallback=(consumerTag, message)->{
            //接受消息前进行沉睡,1秒
            SleepUtils.sleep(1);
 
            System.out.println("接收到的消息:"+new String(message.getBody(),"UTF-8"));
 
            //应答消息,手动应答
            channel.basicAck(message.getEnvelope().getDeliveryTag(),false);
        };
 
        //消息被中断声明
        CancelCallback cancelCallback=(consumerTag)->{
            System.out.println(consumerTag+"消息消费被中断");
        };
 
        //设置不公平分发
        //prefetchCont为1,表示开启不公平分发,大于1,则表示预取值
        //预取值为2
        int prefetchCont=5;
        channel.basicQos(prefetchCont);
 
        //采用手动应答
        boolean autoAck=false;
        channel.basicConsume(TASK_NAME,autoAck,deliverCallback,cancelCallback);
 
    }
 
}

(5).创建处理慢的消费者

    ①获取信道连接

    ②消息接收回调函数,调用睡眠工具类,睡眠30秒,调用basicAck(消息标记,是否批量应答)方法自动应答

    ③消息中断回调函数调用

    ④调用basicQos(分发数量)方法设置不公平分发,设置数量为2

    ⑤接收消息
public class Worker04 {
 
    //队列
    public static final  String TASK_NAME="ack_queue";
 
    public static void main(String[] args) throws Exception {
 
        Channel channel = RabbitUtils.getChannel();
 
        System.out.println("C2等待接收处理,时间较长......");
 
        //声明,接受消息
        DeliverCallback deliverCallback=(consumerTag, message)->{
            //接受消息前进行沉睡,30秒
            SleepUtils.sleep(30);
 
            System.out.println("接收到的消息:"+new String(message.getBody(),"UTF-8"));
 
            //应答消息,手动应答
            channel.basicAck(message.getEnvelope().getDeliveryTag(),false);
        };
 
        //消息被中断声明
        CancelCallback cancelCallback=(consumerTag)->{
            System.out.println(consumerTag+"消息消费被中断");
        };
 
        //设置不公平分发
        //预取值为5
        int prefetchCont=2;
        channel.basicQos(prefetchCont);
 
        //采用手动应答
        boolean autoAck=false;
        channel.basicConsume(TASK_NAME,autoAck,deliverCallback,cancelCallback);
 
    }
 
}

(6)测试,启动生产者和消费者

微信截图_20220310115516.png

微信截图_20220310115524.png

微信截图_20220310115619.png

C1处理3秒后完成三个消息接受,C2一共处理60秒完成两个消息接收