work消息模型-rabbitMq消息模型(二)

1. 工作隊列或者競爭消費者模式

在這裏插入圖片描述上篇基本消息模型中是從一個命名隊列中發送並接受消息。在這裏,將創建一個工作隊列,在多個工作者之間分配任務。

工作隊列,又稱任務隊列。主要思想就是避免執行資源密集型任務時,必須等待它執行完成。相反我們稍後完成任務,我們將任務封裝爲消息並將其發送到隊列。 在後臺運行的工作進程將獲取任務並最終執行作業。當你運行許多工人時,任務將在他們之間共享,但是一個消息只能被一個消費者獲取。

這個概念在Web應用程序中特別有用,因爲在短的HTTP請求窗口中無法處理複雜的任務。

接下來我們來模擬這個流程:

​ P:生產者:任務的發佈者

​ C1:消費者,領取任務並且完成任務,假設完成速度較快

​ C2:消費者2:領取任務並完成任務,假設完成速度慢

那麼如何避免消息堆積?

1) 採用workqueue,多個消費者監聽同一隊列。

2)接收到消息以後,而是通過線程池,異步消費。

2. 生產者

發送50條消息。

// 生產者
public class Send {
    private final static String QUEUE_NAME = "test_work_queue";

    public static void main(String[] argv) throws Exception {
        // 獲取到連接
        Connection connection = ConnectionUtil.getConnection();
        // 獲取通道
        Channel channel = connection.createChannel();
        // 聲明隊列
        channel.queueDeclare(QUEUE_NAME, false, false, false, null);
        // 循環發佈任務
        for (int i = 0; i < 50; i++) {
            // 消息內容
            String message = "task .. " + i;
            channel.basicPublish("", QUEUE_NAME, null, message.getBytes());
            System.out.println(" [x] Sent '" + message + "'");

            Thread.sleep(i * 2);
        }
        // 關閉通道和連接
        channel.close();
        connection.close();
    }
}

3. 消費者1

// 消費者1
public class Recv {
    private final static String QUEUE_NAME = "test_work_queue";

    public static void main(String[] argv) throws Exception {
        // 獲取到連接
        Connection connection = ConnectionUtil.getConnection();
        // 獲取通道
        final Channel channel = connection.createChannel();
        // 聲明隊列
        channel.queueDeclare(QUEUE_NAME, false, false, false, null);
        // 設置每個消費者同時只能處理一條消息
        channel.basicQos(1);
        // 定義隊列的消費者
        DefaultConsumer consumer = new DefaultConsumer(channel) {
            // 獲取消息,並且處理,這個方法類似事件監聽,如果有消息的時候,會被自動調用
            @Override
            public void handleDelivery(String consumerTag, Envelope envelope, BasicProperties properties,
                    byte[] body) throws IOException {
                // body 即消息體
                String msg = new String(body);
                System.out.println(" [消費者1] received : " + msg + "!");
                try {
                    // 模擬完成任務的耗時:1000ms
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                }
                // 手動ACK
                channel.basicAck(envelope.getDeliveryTag(), false);
            }
        };
        // 監聽隊列。
        channel.basicConsume(QUEUE_NAME, false, consumer);
    }
}

注意: 消費者1沒處理一條消息延時了一秒時間。

3. 消費者2

//消費者2
public class Recv2 {
    private final static String QUEUE_NAME = "test_work_queue";

    public static void main(String[] argv) throws Exception {
        // 獲取到連接
        Connection connection = ConnectionUtil.getConnection();
        // 獲取通道
        final Channel channel = connection.createChannel();
        // 聲明隊列
        channel.queueDeclare(QUEUE_NAME, false, false, false, null);
        // 設置每個消費者同時只能處理一條消息
        channel.basicQos(1);
        // 定義隊列的消費者
        DefaultConsumer consumer = new DefaultConsumer(channel) {
            // 獲取消息,並且處理,這個方法類似事件監聽,如果有消息的時候,會被自動調用
            @Override
            public void handleDelivery(String consumerTag, Envelope envelope, BasicProperties properties,
                    byte[] body) throws IOException {
                // body 即消息體
                String msg = new String(body);
                System.out.println(" [消費者2] received : " + msg + "!");
                // 手動ACK
                channel.basicAck(envelope.getDeliveryTag(), false);
            }
        };
        // 監聽隊列。
        channel.basicConsume(QUEUE_NAME, false, consumer);
    }
}

與消費者1基本類似,就是沒有設置消費耗時時間。

這裏是模擬有些消費者快,有些比較慢。

接下來,兩個消費者一同啓動,然後讓生產者發送50條消息:
0-49
在這裏插入圖片描述在這裏插入圖片描述在這裏插入圖片描述
可以發現,兩個消費者各自消費了25條消息,而且各不相同,這就實現了任務的分發。
但還有一個現象就是消費者2早早就完成了任務,消費者還在每隔一秒消費一個消息(人工仿造業務處理慢的場景) ,這樣就造成了性能的浪費。如何解決?

4. 能者多勞

  • 消費者1比消費者2的效率要低,一次任務的耗時較長
  • 然而兩人最終消費的消息數量是一樣的
  • 消費者2大量時間處於空閒狀態,消費者1一直忙碌

現在的狀態屬於是把任務平均分配,正確的做法應該是消費越快的人,消費的越多。

怎麼實現呢?
可以使用basicQos方法和prefetchCount = 1設置。 這告訴RabbitMQ一次不要向工作人員發送多於一條消息。 或者換句話說,不要向工作人員發送新消息,直到它處理並確認了前一個消息。 相反,它會將其分派給不是仍然忙碌的下一個工作人員。

在這裏插入圖片描述
消費者一和消費者二分別做上述的設置後,再次執行發送50條消息,看2個消費者的消費情況:
在這裏插入圖片描述
在這裏插入圖片描述
可以看到配置有了明顯效果。

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