RabbitMq——工作队列工作模式

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

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)测试,启动生产者和两个消费者

RabbitMq——工作队列工作模式_第1张图片

af8ddf570f354725b68369bff6c98dcf.png 

21fe802d147d4452828fdd25ea9c25de.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)测试,启动生产者和消费者

RabbitMq——工作队列工作模式_第2张图片

2ac5a647c154407daef50abb798c8b54.png 

a9167494675f4aa082e0ac35aac6ae7c.png 

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

 

 

你可能感兴趣的:(rabbitmq,分布式)