AMQP-RabbitMQ/2/工作隊列

2. 工作隊列 Work queues

Distributing tasks among workers

消息將發送給c1或者c2
image.png

# 個人理解

  • 生產者定義Queue,並向該隊列發送消息

  • 多個消費者可以從指定的同一個Queue中讀取消息。每條消息只會發送給其中某一個消費者。

  • 生產者

package com.futao.springmvcdemo.mq.rabbit.workqueue;

import com.futao.springmvcdemo.mq.rabbit.RabbitMqConnectionTools;
import com.futao.springmvcdemo.mq.rabbit.RabbitMqQueueEnum;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import lombok.Cleanup;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;

/**
 * 簡單發送者
 *
 * @author futao
 * Created on 2019-04-22.
 */
@Slf4j
public class Send {
    @SneakyThrows
    public static void main(String[] args) {
        @Cleanup
        Connection connection = RabbitMqConnectionTools.getConnection();
        @Cleanup
        Channel channel = connection.createChannel();
        //開啓持久化消息
        boolean durable = true;
        //定義一個隊列
        channel.queueDeclare(RabbitMqQueueEnum.WORK_QUEUE.getQueueName(), durable, false, false, null);
        String msg = "Hello RabbitMq!";
        for (int i = 0; i < 20; i++) {
            channel.basicPublish("", RabbitMqQueueEnum.WORK_QUEUE.getQueueName(), null, (msg + i).getBytes());
            log.info("Send msg:[{}] success", (msg + i));
        }
    }
}
  • 消費者1 - 每1秒處理一條
package com.futao.springmvcdemo.mq.rabbit.workqueue;

import com.futao.springmvcdemo.mq.rabbit.RabbitMqConnectionTools;
import com.futao.springmvcdemo.mq.rabbit.RabbitMqQueueEnum;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.DeliverCallback;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;

/**
 * 簡單消費者
 *
 * @author futao
 * Created on 2019-04-22.
 */
@Slf4j
public class RecvOne {
    @SneakyThrows
    public static void main(String[] args) {
        Channel channel = RabbitMqConnectionTools.getChannel();
        //開啓持久化消息
        boolean durable = true;
        channel.queueDeclare(RabbitMqQueueEnum.WORK_QUEUE.getQueueName(), durable, false, false, null);
        log.info("Waiting for message...");
        DeliverCallback deliverCallback = ((consumerTag, message) -> {
            log.info("收到消息:[{}],tag:[{}]", new String(message.getBody()), consumerTag);
            //acknowledgment應答
            channel.basicAck(message.getEnvelope().getDeliveryTag(), false);
            try {
                Thread.sleep(1000);
            } catch (Exception e) {

            }
        });
        //關閉自動應答
        boolean autoAck = false;
        channel.basicConsume(RabbitMqQueueEnum.WORK_QUEUE.getQueueName(), autoAck, deliverCallback, consumerTag -> {
        });
    }
}
  • 消費者2 - 每2秒處理一條
package com.futao.springmvcdemo.mq.rabbit.workqueue;

import com.futao.springmvcdemo.mq.rabbit.RabbitMqConnectionTools;
import com.futao.springmvcdemo.mq.rabbit.RabbitMqQueueEnum;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.DeliverCallback;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;

/**
 * 簡單消費者
 *
 * @author futao
 * Created on 2019-04-22.
 */
@Slf4j
public class RecvTwo {
    @SneakyThrows
    public static void main(String[] args) {
        Channel channel = RabbitMqConnectionTools.getChannel();
        //開啓持久化消息
        boolean durable = true;
        channel.queueDeclare(RabbitMqQueueEnum.WORK_QUEUE.getQueueName(), durable, false, false, null);
        log.info("Waiting for message...");
        DeliverCallback deliverCallback = ((consumerTag, message) -> {
            log.info("收到消息:[{}],tag:[{}]", new String(message.getBody()), consumerTag);
            //acknowledgment應答
            channel.basicAck(message.getEnvelope().getDeliveryTag(), false);
            try {
                Thread.sleep(2000);
            } catch (Exception e) {

            }
        });
        //關閉自動應答
        boolean autoAck = false;
        channel.basicConsume(RabbitMqQueueEnum.WORK_QUEUE.getQueueName(), autoAck, deliverCallback, consumerTag -> {
        });
    }
}
  • 結果
    • 生產者
      生產者日誌
    • 消費者1日誌
      消費者1日誌
    • 消費者2日誌
      消費者2日誌
  • 特點: 多個消費者之間,不論消息的處理速度,都是平均分發(公平分發)。你一個,我一個,他一個。此時是公平隊列
  • 注意:
    • 定義隊列的時候,設置是否開啓消息的持久化(該設置需要同時在生產者和消費者設置)
       //開啓持久化消息
        boolean durable = true;
        channel.queueDeclare(RabbitMqQueueEnum.WORK_QUEUE.getQueueName(), durable, false, false, null);
  • 如果消息隊列已經存在,則不可以修改相應的配置,必須刪除原有的隊列,或者新建一個新的隊列。
  • 關閉自動應答(開啓手動應答),可以防止消息在未被正確消費的情況下被Rabbitmq從隊列內存中刪除。

# 實現工作隊列下的非公平隊列

消費者設置一次只發送一條消息,並且在被正確消費之前發繼續發送下一條消息。從而使得消費快的消費者比消費慢的消費者消費更多的消息

        //告訴rabbitmq一次只發送一條消息,並且在前一個消息未被處理或者消費之前,不繼續發送下一個消息
        channel.basicQos(1);
  • 測試結果
    image.png
    此時明顯打破了消息的公平分發,消費快的消費者接收到的消息更多。
    如果有兩個消費者,其中一個ConsumerA設置了Qos=1,另一個ConsumerB沒有設置。經過我的測試,ConsumerA會獲得大量的消息,都積壓在ConsumerA,而ConsumerB獲得消息很少。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章