Skip to content

Introduction to Message Queues

DodaTech Updated 2026-06-28 6 min read

In this tutorial, you will learn about Introduction to Message Queues. We cover key concepts, practical examples, and best practices to help you master this topic.

Message queues enable asynchronous communication between distributed services by temporarily storing messages until consumers are ready to Process them, decoupling producers and consumers for resilience and scalability.

What You'll Learn

By the end of this lesson, you will understand what message queues are, why they matter in Distributed Systems, and the core concepts of producers, consumers, queues, and brokers.

Why It Matters

Without message queues, a spike in traffic can overwhelm your backend services directly. If one service is down, requests to it fail immediately. Message queues buffer the load and decouple services so failures in one part of the system do not cascade. Doda Browser uses message queues to decouple file upload from malware analysis, ensuring no file is lost even when the analysis cluster is under load.

Real-World Use

An e-commerce platform processes thousands of orders per minute. Each order triggers inventory checks, payment processing, shipping label generation, and notification emails. A message queue holds each order as a message. Workers pick messages they can handle — email workers send confirmations, shipping workers print labels. If email service is slow, orders still flow to shipping without waiting.

How Message Queues Work

flowchart LR
    P[Producer] -->|Publish| Q[Queue / Broker]
    Q -->|Deliver| C1[Consumer 1]
    Q -->|Deliver| C2[Consumer 2]
    Q -->|Deliver| C3[Consumer N]
    style Q fill:#f90,color:#fff

A producer sends a message to a queue. The broker stores the message until a consumer is ready. When a consumer requests the next message, the broker delivers it. After processing, the consumer acknowledges completion so the broker can remove the message.

Core Concepts

Producer: The application that creates and sends messages. It does not wait for a response.

Consumer: The application that receives and processes messages. It pulls messages from the queue at its own pace.

Queue: A buffer that stores messages until consumers process them. Queues can be in memory or persisted to disk.

Broker: The server that manages queues, routes messages, and ensures delivery. Examples include RabbitMQ, Apache Kafka, and Amazon SQS.

Message: The data unit sent from producer to consumer. It can be JSON, XML, binary, or any format the applications agree on.

First Queue with Python

import pika

connection = pika.BlockingConnection(pika.ConnectionParameters('localhost'))
channel = connection.channel()

channel.queue_declare(queue='hello')

channel.basic_publish(
    exchange='',
    routing_key='hello',
    body='Hello DodaTech!'
)

print("Message sent to queue 'hello'")
connection.close()

Expected output:

Message sent to queue 'hello'

The producer connects to RabbitMQ, declares a queue named hello, and publishes a message. If hello does not exist, it is created automatically.

import pika

connection = pika.BlockingConnection(pika.ConnectionParameters('localhost'))
channel = connection.channel()

channel.queue_declare(queue='hello')

def callback(ch, method, properties, body):
    print(f"Received: {body.decode()}")

channel.basic_consume(
    queue='hello',
    on_message_callback=callback,
    auto_ack=True
)

print('Waiting for messages. Press Ctrl+C to exit.')
channel.start_consuming()

Expected output:

Waiting for messages. Press Ctrl+C to exit.
Received: Hello DodaTech!

The consumer connects to the same queue. When a message arrives, the callback function processes it. The consumer keeps running, waiting for new messages.

Why Queues Decouple Services

Think of a message queue like a mailbox. You drop a letter in the mailbox and walk away. The postal service delivers it when the recipient is available. You do not have to wait for them to open the door.

In software terms, the producer does not need to know anything about the consumer — not its address, not its availability, not how many instances exist. This decoupling means you can scale each side independently. If traffic spikes, you add more consumers. If consumers are slow, the queue absorbs the overflow.

Common Mistakes

1. Treating Queues Like Databases

Queues are not designed for long-term storage. Messages are meant to be consumed and removed. If you need to retain data, store it in a database and send only a reference in the message.

2. Using Queues for Request-Response

Message queues are asynchronous by nature. If you need a synchronous response, use HTTP or RPC. Queues add latency and complexity for immediate responses.

3. Ignoring Queue Depth

Messages pile up in a queue if consumers cannot keep up. Without monitoring queue depth, you discover the problem only when processing delays become hours or days.

4. Assuming Exactly-Once Delivery

Most message systems guarantee at-least-once delivery, not exactly-once. Your consumer must handle duplicate messages gracefully through idempotency.

5. Sending Oversized Messages

Message brokers have size limits. RabbitMQ defaults to 128MB, Kafka to 1MB. Large messages consume memory and slow the broker. Send references to blob storage instead.

Practice Questions

1. What is the difference between a producer and a consumer?

A producer sends messages to a queue. A consumer receives and processes messages from the queue. They operate independently — the producer does not wait for the consumer.

2. How does a queue provide fault tolerance?

If a consumer crashes, messages remain in the queue. When the consumer restarts, it continues processing from where it left off. If the producer crashes, already-sent messages are safe in the queue.

3. What happens if consumers are slower than producers?

The queue depth grows. Messages accumulate until consumers catch up. This is called backlog. Monitoring queue depth alerts you when consumers are falling behind.

4. Can a queue have multiple producers?

Yes. Multiple producers can send messages to the same queue concurrently. The broker handles concurrency and ensures each message is stored exactly once.

Challenge

Design a message queue setup for a photo upload service: users upload photos, each photo needs thumbnail generation, virus scanning, and EXIF data extraction. All three tasks should run in parallel after upload.

FAQ

What is a message broker?

A message broker is a server that manages queues, routes messages between producers and consumers, and ensures reliable delivery. RabbitMQ, Apache Kafka, and Amazon SQS are popular brokers.

Can messages be lost if the broker crashes?

Yes, if messages are only in memory. Use persistent queues and publisher confirms to ensure messages survive broker restarts. Most brokers support disk persistence.

What is the difference between a queue and a topic?

A queue delivers each message to one consumer (point-to-point). A topic delivers each message to all subscribed consumers (publish-subscribe).

How many consumers should I run?

Start with 2-4 consumers per queue. Monitor queue depth and consumer utilization. Add consumers when messages accumulate faster than they are processed.

What serialization format should I use for messages?

JSON is the most common for readability and language-agnostic support. For high-throughput systems, consider Avro or Protocol Buffers for smaller payloads and faster serialization.

Mini Project: Basic Queue Round-Trip

import pika
import json
import threading
import time

def consumer_thread():
    connection = pika.BlockingConnection(pika.ConnectionParameters('localhost'))
    channel = connection.channel()
    channel.queue_declare(queue='demo_queue')

    def callback(ch, method, properties, body):
        data = json.loads(body)
        print(f"[Consumer] Received: {data}")
        ch.basic_ack(delivery_tag=method.delivery_tag)

    channel.basic_consume(queue='demo_queue', on_message_callback=callback)
    channel.start_consuming()

consumer = threading.Thread(target=consumer_thread, daemon=True)
consumer.start()

time.sleep(1)

connection = pika.BlockingConnection(pika.ConnectionParameters('localhost'))
channel = connection.channel()
channel.queue_declare(queue='demo_queue')

for i in range(5):
    msg = {'id': i, 'text': f'Message {i}'}
    channel.basic_publish(
        exchange='',
        routing_key='demo_queue',
        body=json.dumps(msg),
        properties=pika.BasicProperties(delivery_mode=2)
    )
    print(f"[Producer] Sent: {msg}")

connection.close()
time.sleep(1)

Expected output:

[Producer] Sent: {'id': 0, 'text': 'Message 0'}
[Producer] Sent: {'id': 1, 'text': 'Message 1'}
[Consumer] Received: {'id': 0, 'text': 'Message 0'}
[Producer] Sent: {'id': 2, 'text': 'Message 2'}
[Consumer] Received: {'id': 1, 'text': 'Message 1'}
[Producer] Sent: {'id': 3, 'text': 'Message 3'}
[Consumer] Received: {'id': 2, 'text': 'Message 2'}
[Producer] Sent: {'id': 4, 'text': 'Message 4'}
[Consumer] Received: {'id': 3, 'text': 'Message 3'}
[Consumer] Received: {'id': 4, 'text': 'Message 4'}

What's Next

Now that you understand queue basics, explore the point-to-point pattern for one-to-one communication, or jump to publish-subscribe pattern for broadcasting events to multiple consumers.

Built by the developers of DodaTech

Doda Browser, DodaZIP & Durga Antivirus Pro