RabbitMQ——work queue

工作隊列

在這裏插入圖片描述
包含一個生產者和多個消費者的MQ。生產者發送消息非常輕鬆,消費者和業務結合,需要花費時間。
下面介紹一種輪詢方式的工作隊列:

首先定義生產者:

public class Send {

    private static final String QUEUE_NAME = "work_queue";

    public static void main(String[] args) throws IOException, TimeoutException, InterruptedException {
        Connection connection = RabbitConnection.getConnection();
        Channel channel = connection.createChannel();
        channel.queueDeclare(QUEUE_NAME, false, false, false, null);
        for(int i = 0; i<50; i++){
            String message = "work+"+i;

            channel.basicPublish("", QUEUE_NAME, null, message.getBytes());
            Thread.sleep(i*20);
        }
    }
}

生產50條消息,給下面的兩個消費者進行食用:

消費者One(這裏我們消費一條消息就sleep 1s

public class ReceiveOne {
    private static final String QUEUE_NAME = "work_queue";

    public static void main(String[] args) throws IOException, TimeoutException {
        Connection connection = RabbitConnection.getConnection();
        Channel channel = connection.createChannel();
        channel.queueDeclare(QUEUE_NAME, false, false, false, null);

        DeliverCallback deliverCallback = (consumerTag, delivery) -> {
            String message = new String(delivery.getBody(), "UTF-8");
            System.out.println(" [ConsumerOne is] Received '" + message + "'");
            try {
                Thread.sleep(1000);   
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        };
        channel.basicConsume(QUEUE_NAME, true, deliverCallback, consumerTag -> { });
    }
}

消費者Two (sleep 2s)

public class ReceiveTwo {

    private static final String QUEUE_NAME = "work_queue";

    public static void main(String[] args) throws IOException, TimeoutException {
        Connection connection = RabbitConnection.getConnection();
        Channel channel = connection.createChannel();
        channel.queueDeclare(QUEUE_NAME, false, false, false, null);

        DeliverCallback deliverCallback = (consumerTag, delivery) -> {
            String message = new String(delivery.getBody(), "UTF-8");
            System.out.println(" [ConsumerTwo is] Received '" + message + "'");
            try {
                Thread.sleep(2000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        };
        channel.basicConsume(QUEUE_NAME, true, deliverCallback, consumerTag -> { });
    }
}

下面我們看兩個控制檯的輸出:

ReceiveOne
在這裏插入圖片描述
ReceiveTwo
在這裏插入圖片描述

事實證明,設置兩個不同的sleep時間,並沒有影響兩個消費者輪流消費,這種方式稱之爲輪詢。

這樣看起來不太合理,消費快的消費者理應多消費一些,所謂“能者多勞”,爲了解決這個問題,下面我們引入了公平分發:

公平分發,它是由消費者主動發送ACK應答,告訴生產者:“我已經消費完了,快點給我下一個”,之前的都是,自動發送ACK,並非手動的。下面我們實現一下手動的。

只需要在前面的代碼修改部分即可:

發送者:

public class Send {

    private static final String QUEUE_NAME = "work_queue";

    public static void main(String[] args) throws IOException, TimeoutException, InterruptedException {
        Connection connection = RabbitConnection.getConnection();
        Channel channel = connection.createChannel();
        channel.queueDeclare(QUEUE_NAME, false, false, false, null);

        // 消費者發送確認消息(ACK)之前,只發送一個消息給你
        channel.basicQos(1);

        for(int i = 0; i<50; i++){
            String message = "work+"+i;

            channel.basicPublish("", QUEUE_NAME, null, message.getBytes());
            Thread.sleep(i*20);
        }
    }
}

消費者1:

public class ReceiveOne {
    private static final String QUEUE_NAME = "work_queue";

    public static void main(String[] args) throws IOException, TimeoutException {
        Connection connection = RabbitConnection.getConnection();
        Channel channel = connection.createChannel();
        channel.queueDeclare(QUEUE_NAME, false, false, false, null);

        channel.basicQos(1);  //需要修改

        DeliverCallback deliverCallback = (consumerTag, delivery) -> {
            String message = new String(delivery.getBody(), "UTF-8");
            System.out.println(" [ConsumerOne is] Received '" + message + "'");
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }finally {
                channel.basicAck(delivery.getEnvelope().getDeliveryTag(),false);  //這裏是手動應答
            }
        };
        boolean autoAck = false;  //這裏需要修改至手動應答 
        channel.basicConsume(QUEUE_NAME, autoAck, deliverCallback, consumerTag -> { });
    }
}

同理2也是一樣的,這裏就不再重複了。
在這裏插入圖片描述
在這裏插入圖片描述
很明顯,消費者1的處理速度比消費者2要快,因爲消費者1的sleep時間較短,這就和上面的輪詢立判高下了,體現了能者多勞。


下面我們有一個問題,我們設置 boolean autoAck = false;這個是有隱患的,假設我們我的消費者1掛了,而消息已經發送過去了,這時候,我們該怎麼辦?這時候這個消息是丟失的。爲了應對這種情況,我們可以設置autoack = true,能解決問題。但是我們又想公平分發,這就有點麻煩了,怎麼解決這個問題,暫時沒有想法,容我繼續學習。

還有一個問題,如果RabbitMQ掛了,這時候怎麼辦?這裏我們就要設置另外一個參數了,那就是durable,表示可持久化。

在聲明隊列的時候,就要設置好,等隊列已經存在了再設置的話,就會報錯。這點注意,如果已經報錯了,就刪除隊列重新聲明。

 boolean durable = false; //聲明數據可以持久化
 channel.queueDeclare(QUEUE_NAME, durable, false, false, null);
發佈了138 篇原創文章 · 獲贊 42 · 訪問量 9萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章