RabbitMQ學習系列(三):工作隊列詳解

(一)RabbitMQ工作隊列模型結構

工作隊列的模型相比簡單隊列增加了消費者的數量。

生產者提供消息到消息隊列中,消費者可以去獲取隊列中的消息。在工作隊列中默認採用輪詢分發的方式將消息分發給消費者。所謂輪詢分發,就是指不管消費者處理消息的速度是快是慢,都按照順序輪流把消息發給消費者。

(二)工作隊列實踐(輪詢分發)

使用工作隊列的代碼和簡單隊列基本一致,只不過多了幾個消費者

2.1 創建工具類

創建工具類的代碼在前一篇博客中已經講到了,這裏直接貼上代碼:

public class ConnectionUtil {
    public static Connection getConnection() throws IOException, TimeoutException {
        //定義一個連接工廠
        ConnectionFactory factory=new ConnectionFactory();
        //設置服務地址
        factory.setHost("127.0.0.1");
        //設置AMQP端口
        factory.setPort(5672);
        //設置VHOSTS
        factory.setVirtualHost("/vhosts_sdxb");
        //設置用戶名
        factory.setUsername("user_sdxb");
        factory.setPassword("123456");
        return factory.newConnection();
    }
}

2.2 創建生產者

public class Send {
    private static final String QUEUE_NAME="work_queue";
    public static void main(String[] args) throws IOException, TimeoutException {
        //1.獲取連接
        Connection connection = ConnectionUtil.getConnection();
        //2.創建通道
        Channel channel = connection.createChannel();
        //3.創建隊列聲明
        channel.queueDeclare(QUEUE_NAME,false,false,false,null);
        for (int i=0;i<50;i++){
            String msg="i="+i;
            channel.basicPublish("",QUEUE_NAME,null,msg.getBytes());
        }
        channel.close();
        connection.close();
    }
}

2.3 創建消費者一

爲了體現消費者處理消息的快慢,我在兩個消費者中分別設置線程休眠1s和2s

public class Receive1 {
    private static final String QUEUE_NAME="work_queue";
    public static void main(String[] args) throws IOException, TimeoutException {
        //獲取連接
        Connection connection = ConnectionUtil.getConnection();
        //創建通道
        Channel channel = connection.createChannel();
        //創建隊列聲明
        channel.queueDeclare(QUEUE_NAME, false, false, false, null);
        //創建消費者監聽方法
        Consumer consumer=new DefaultConsumer(channel){
            @Override
            public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
                String msg=new String(body,"utf-8");
                System.out.println(msg);
                try {
                    //設置睡眠實踐1s
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        };
        //監聽隊列
        channel.basicConsume(QUEUE_NAME,true,consumer);
    }
}

2.4 創建消費者二

public class Receive2 {
    private static final String QUEUE_NAME="work_queue";
    public static void main(String[] args) throws IOException, TimeoutException {
        Connection connection = ConnectionUtil.getConnection();
        Channel channel = connection.createChannel();
        channel.queueDeclare(QUEUE_NAME, false, false, false, null);
        Consumer consumer=new DefaultConsumer(channel){
            @Override
            public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
                String msg=new String(body,"utf-8");
                System.out.println(msg);
                try {
                    //設置睡眠時間2s
                    Thread.sleep(2000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        };
        channel.basicConsume(QUEUE_NAME,true,consumer);
    }
}

分別將兩個消費者運行起來,然後運行生產者發送50條消息,可以發現雖然兩個消費者處理消息的能力有快有慢,但是得到的消息都是25條,下面展示消費者1獲取的消息部分截圖。

(三)公平分發(Fair dispatch)

在某些場景下輪詢分發是不合理的,因此工作隊列還有公平分發的方式,所謂公平分發,就是能者多勞,處理消息快的人獲得消息多,處理消息慢的人獲得消息少。公平分發的實現只需要對代碼做一些修改:

3.1 修改生產者

對於生產者,只需要對通道增加一條限制,限制通道發送給同一個消費者不得超過一條消息,也就是隻有當消費者處理完一條消息以後纔會發第二條消息給它。使用channel.basicQos();方法,設置參數爲1表示限制一次不超過1條消息。

public class Send {
    private static final String QUEUE_NAME="work_queue_fair";
    public static void main(String[] args) throws IOException, TimeoutException {
        Connection connection = ConnectionUtil.getConnection();
        Channel channel = connection.createChannel();
        channel.queueDeclare(QUEUE_NAME,false,false,false,null);
        //限制通道發送給同一個消費者不得超過一條消息
        int prefenchCount=1;
        channel.basicQos(prefenchCount);
        for (int i=0;i<50;i++){
            String msg="i="+i;
            channel.basicPublish("",QUEUE_NAME,null,msg.getBytes());
        }
        channel.close();
        connection.close();
    }
}

3.2 修改消費者

對於消費者,需要修改三處地方,第一處和生產者一樣修改通道的限制信息;第二處關閉消費者的自動應答;第三處設置手動回執,即處理完一條消息後手動發送處理完成的指令給隊列。

//保證一次只分發一次
channel.basicQos(1);
//設置手動回執
channel.basicAck(envelope.getDeliveryTag(),false);
//關閉自動應答
boolean autoAck=false;
channel.basicConsume(QUEUE_NAME,autoAck,consumer);

以下是修改後的消費者代碼

public class Receive1 {
    private static final String QUEUE_NAME="work_queue_fair";
    public static void main(String[] args) throws IOException, TimeoutException {
        Connection connection = ConnectionUtil.getConnection();
        final Channel channel = connection.createChannel();
        channel.queueDeclare(QUEUE_NAME, 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 {
                String msg=new String(body,"utf-8");
                System.out.println(msg);
                //設置手動回執
                channel.basicAck(envelope.getDeliveryTag(),false);
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        };
        //關閉自動應答
        boolean autoAck=false;
        channel.basicConsume(QUEUE_NAME,autoAck,consumer);
    }
}

設置完之後工作隊列就變成了公平分發方式,測試結果:

3.3 關於自動應答

在前面修改消費者代碼的時候,我們關閉了自動應答

boolean autoAck=false;
channel.basicConsume(QUEUE_NAME,autoAck,consumer);

這是basicConsume的第二個參數

autoAck=true時,表示開啓自動應答,一旦rabbitmq將隊列中的消息發送給消費者,這個消息就會從隊列中消失。但是如果此時消費者掛掉了,那麼這條消息也就徹底消失了。

autoAck=false時,關閉自動應答,rabbitmq將隊列中的消息發送給消費者,只有當消費者返回確認之後,隊列中的消息纔會被刪除。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章