RabbitMQ Messaging & Async Systems · 课时

竞争消费者模式

实现竞争消费者模式,使多个消费者能够从同一个队列中处理消息。通过增加消费者数量来扩展处理能力。

第 1 / 4 课11 个步骤

竞争消费者模式 是 CoddyKit 上的免费 RabbitMQ Messaging & Async Systems 课时。 这是第 1 节课,共 4 节。 你可以在下方免费阅读本课时的完整内容 — 然后在浏览器中使用内置代码编辑器和全天候 AI 导师进行实践。 这是 RabbitMQ Messaging & Async Systems 学习路径的一部分,你的进度在网页和 CoddyKit 应用中同步。 RabbitMQ Messaging & Async Systems 课程共包含 4 节课。

本课时的部分内容尚未翻译,以英文显示。

Scaling with Competing Consumers

Welcome! In distributed systems, you often need to process many tasks efficiently. The Competing Consumers pattern is a powerful way to achieve this.

It allows you to scale your message processing capacity by simply adding more workers.

How Competing Consumers Work

Imagine a single queue of tasks. Instead of one worker taking all tasks, multiple workers (consumers) listen to this same queue.

  • Each message is delivered to only one of the competing consumers.
  • Consumers "compete" to receive the next available message.
  • This distributes the workload automatically.

Key Benefits of the Pattern

The Competing Consumers pattern offers several advantages:

  • Scalability: Easily increase processing power by adding more consumer instances.
  • Reliability: If one consumer fails, others can pick up its share of messages.
  • Load Balancing: Messages are spread across available consumers, balancing the workload.
  • Decoupling: Producers don't need to know how many consumers there are or where they are.

Producer: Sending Tasks

Let's set up a basic producer that sends messages (tasks) to a queue named task_queue. Each message will be a simple string.

Run this code to send a few messages:

import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;

public class TaskProducer {
    private final static String QUEUE_NAME = "task_queue";

    public static void main(String[] argv) throws Exception {
        ConnectionFactory factory = new ConnectionFactory();
        factory.setHost("localhost"); // Assuming RabbitMQ is local

        try (Connection connection = factory.newConnection();
             Channel channel = connection.createChannel()) {
            // Declare a durable queue
            channel.queueDeclare(QUEUE_NAME, true, false, false, null);

            for (int i = 0; i < 10; i++) {
                String message = "Task " + (i + 1);
                channel.basicPublish("", QUEUE_NAME, null, message.getBytes("UTF-8"));
                System.out.println(" [x] Sent '" + message + "'");
                Thread.sleep(100); // Small delay to visualize
            }
        }
    }
}

Consumer 1: Processing Tasks

Now, let's create our first consumer. It will connect to task_queue and start processing messages. Each message will be acknowledged after simulating work.

Run this consumer in a terminal:

import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;
import com.rabbitmq.client.DeliverCallback;

public class TaskConsumer {
    private final static String QUEUE_NAME = "task_queue";

    public static void main(String[] argv) throws Exception {
        ConnectionFactory factory = new ConnectionFactory();
        factory.setHost("localhost");

        Connection connection = factory.newConnection();
        Channel channel = connection.createChannel();

        channel.queueDeclare(QUEUE_NAME, true, false, false, null);
        System.out.println(" [*] Consumer 1 waiting for messages.");

        // Basic QoS: Prefetch 1 message at a time to ensure fair dispatch
        channel.basicQos(1);

        DeliverCallback deliverCallback = (consumerTag, delivery) -> {
            String message = new String(delivery.getBody(), "UTF-8");
            System.out.println(" [C1] Received '" + message + "'");
            try {
                Thread.sleep(1000); // Simulate work
            } finally {
                System.out.println(" [C1] Done '" + message + "'");
                channel.basicAck(delivery.getEnvelope().getDeliveryTag(), false);
            }
        };
        channel.basicConsume(QUEUE_NAME, false, deliverCallback, consumerTag -> {});
    }
}

Running Multiple Consumers

To truly see the competing consumers pattern in action, open a new terminal window and run the exact same TaskConsumer code again.

You'll now have two consumer instances listening to the task_queue. Run the producer code (from Scene 4) once more. Observe how messages are now distributed between both consumer instances, demonstrating how they compete for messages and share the workload!

RabbitMQ's Dispatching Logic

By default, RabbitMQ uses a round-robin dispatching mechanism when multiple consumers are connected to the same queue. This means messages are sent to consumers sequentially.

  • Consumer 1 gets message 1.
  • Consumer 2 gets message 2.
  • Consumer 1 gets message 3, and so on.

The basicQos(1) setting in our consumer code is crucial here. It tells RabbitMQ not to send more than one unacknowledged message to a consumer at a time, ensuring fair dispatch even if consumers process at different speeds.

Common Use Cases

The Competing Consumers pattern is ideal for scenarios like:

  • Image processing: Multiple workers resizing images from a queue.
  • Email sending: Sending bulk emails without overwhelming a single service.
  • Log processing: Analyzing large volumes of logs in parallel.
  • Background jobs: Any task that can be processed independently by multiple workers.

Important Considerations

When using competing consumers, keep these in mind:

  • Message Ordering: If strict message order is critical, this pattern might not be suitable directly, as messages can be processed out of order by different consumers.
  • Idempotency: Consumers should ideally be idempotent. This means processing the same message multiple times should have the same effect as processing it once. This is vital for fault tolerance and retries.

Competing Consumers Quiz

Test your understanding of the Competing Consumers pattern.

Recap: Competing Consumers

Great job! You've learned about the Competing Consumers pattern:

  • It enables multiple consumers to process messages from a single queue.
  • It's excellent for scaling and load balancing message processing.
  • RabbitMQ's default round-robin dispatch and QoS settings facilitate fair distribution.
  • Consider idempotency and potential out-of-order processing for specific use cases.

Next, we'll dive deeper into optimizing consumer efficiency with prefetch counts!

免费开始

用 AI 导师学习 RabbitMQ Messaging & Async Systems — 免费

在浏览器中编写并运行真实代码,获得全天候 AI 导师的即时帮助,并在网页或应用中继续学习。

课程
11
课程
44

常见问题解答

「竞争消费者模式」课时是免费的吗?

是的 — 「竞争消费者模式」的完整文本可在网页上免费阅读。要进行交互式练习(内置代码编辑器和全天候 AI 导师)并解锁 RabbitMQ Messaging & Async Systems 课程的其余内容,请升级到 CoddyKit PRO。 RabbitMQ Messaging & Async Systems 课程共包含 4 节课。

「竞争消费者模式」这节课中我会学到什么?

实现竞争消费者模式,使多个消费者能够从同一个队列中处理消息。通过增加消费者数量来扩展处理能力。 你通过在浏览器中直接运行的动手代码来练习 RabbitMQ Messaging & Async Systems,全天候 AI 导师会在你学习这节课的过程中回答你的问题。

学习 RabbitMQ Messaging & Async Systems 需要有经验吗?

无需任何先前经验。CoddyKit 上的 RabbitMQ Messaging & Async Systems 课程适合初学者到高级学习者,你可以从这里开始或从头开始,按照自己的节奏学习。 这是第 1 节课,共 4 节。

「竞争消费者模式」课时需要多长时间?

大多数 CoddyKit 课程大约需要 5–10 分钟。每节课都很精短且互动,所以你能稳步进步,并在网页和应用中从离开的地方继续。

我能在这节 RabbitMQ Messaging & Async Systems 课中编写并运行代码吗?

能。每节 RabbitMQ Messaging & Async Systems 课都包含内置代码编辑器,你可以在浏览器中直接编写并运行真实代码,并获得即时 AI 反馈 — 无需本地设置。

此课程中的所有课时

  1. 竞争消费者模式
  2. 预取数量(QoS)
  3. 独占消费者与消费者优先级
  4. 单一活跃消费者
← 返回 RabbitMQ Messaging & Async Systems