In this use case, you implement an asynchronous notification workflow where RabbitMQ routes WhatsApp notification messages using a direct exchange with exact-match routing. A Python worker consumes and processes these messages reliably.
In many production systems, sending WhatsApp notifications must be reliable, asynchronous, and independent of the main application flow. RabbitMQ acts as a message broker to decouple message production from consumption.
When to use this pattern:
Use direct exchanges when you need exact-match routing where whatsapp routes only to the WhatsApp queue. This approach provides simple, predictable routing without wildcards, and each message type routes to a specific, dedicated queue.
Comparison:
Use Case 1 (Topic Exchange) provides flexible routing with wildcards, ideal for event streams. Use Case 2 (Direct Exchange) provides exact-match routing, ideal for targeted notifications.
The application publishes a WhatsApp notification message to the notifications exchange. RabbitMQ routes the message to the whatsapp.notifications queue using the exact-match routing key whatsapp. The Python worker then consumes the message from the queue and simulates sending the WhatsApp notification. In production, this would call an external WhatsApp API.
A direct exchange topology is used for exact-match routing. The notifications exchange (type: direct) routes notification messages based on exact routing key matches. The whatsapp.notifications queue is durable, which means it persists messages across broker restarts. The binding connects the exchange to the queue using the whatsapp routing key, ensuring only messages published with this exact key are routed to the queue.
Create the required exchange, queue, and binding for WhatsApp notifications.
Declare the exchange:
./rabbitmqadmin declare exchange \
name=notifications \
type=direct \
durable=true
This creates a durable direct exchange named notifications that routes messages using exact routing keys.
The output is similar to:
exchange declared
Declare the queue:
./rabbitmqadmin declare queue \
name=whatsapp.notifications \
durable=true
This creates a durable queue to persist WhatsApp notification messages until consumed.
The output is similar to:
queue declared
Declare the binding:
./rabbitmqadmin declare binding \
source=notifications \
destination=whatsapp.notifications \
routing_key=whatsapp
This links the exchange to the queue using the whatsapp routing key.
Expected output:
binding declared
Validate that RabbitMQ resources exist and are correctly connected:
./rabbitmqadmin list exchanges name type
./rabbitmqadmin list queues name messages
./rabbitmqadmin list bindings source destination routing_key
These commands verify that the notifications exchange exists (type: direct), the whatsapp.notifications queue exists with zero messages, and the binding connects the exchange to the queue with routing key whatsapp.
The output is similar to:
+---------------+--------+
| name | type |
+---------------+--------+
| notifications | direct |
+---------------+--------+
+------------------------+----------+
| name | messages |
+------------------------+----------+
| whatsapp.notifications | 0 |
+------------------------+----------+
+---------------+------------------------+-------------+
| source | destination | routing_key |
+---------------+------------------------+-------------+
| notifications | whatsapp.notifications | whatsapp |
+---------------+------------------------+-------------+
If you haven’t already installed Python dependencies in Use Case 1, install them now:
sudo zypper install -y python3-pip
pip3 install pika
This installs pip (Python package manager) and pika (RabbitMQ client library for Python).
The worker attaches as a blocking consumer to the whatsapp.notifications queue and processes incoming messages. This worker uses durable queues for message persistence, prefetch_count=1 for fair dispatch, and manual acknowledgments for reliable processing.
Using a text editor, create a whatsapp_worker.py file with the content below:
import pika
import json
import time
RABBITMQ_HOST = "localhost"
QUEUE_NAME = "whatsapp.notifications"
print("[DEBUG] Connecting to RabbitMQ...")
connection = pika.BlockingConnection(
pika.ConnectionParameters(host=RABBITMQ_HOST)
)
channel = connection.channel()
print("[DEBUG] Declaring queue...")
channel.queue_declare(queue=QUEUE_NAME, durable=True)
print("[DEBUG] Setting QoS...")
channel.basic_qos(prefetch_count=1)
print("WhatsApp Worker started. Waiting for messages...")
def send_whatsapp(ch, method, properties, body):
data = json.loads(body.decode())
phone = data.get('phone', 'unknown')
message = data.get('message', '')
print(f"[Worker] Processing WhatsApp notification")
print(f"[Worker] Recipient: {phone}")
print(f"[Worker] Message: {message}")
# Simulate external WhatsApp API call
time.sleep(1)
print("[Worker] WhatsApp notification sent successfully")
ch.basic_ack(delivery_tag=method.delivery_tag)
channel.basic_consume(
queue=QUEUE_NAME,
on_message_callback=send_whatsapp,
auto_ack=False
)
print("[DEBUG] Starting consumer loop (this should block)...")
channel.start_consuming()
Now that you’ve created the worker script, run it in a dedicated terminal session:
python3 whatsapp_worker.py
The worker connects to RabbitMQ and begins listening for WhatsApp notifications. The output is similar to:
[DEBUG] Connecting to RabbitMQ...
[DEBUG] Declaring queue...
[DEBUG] Setting QoS...
WhatsApp Worker started. Waiting for messages...
[DEBUG] Starting consumer loop (this should block)...
The worker blocks at this point, waiting for messages without returning to the shell prompt.
With the worker running, open another SSH terminal and publish a WhatsApp notification message:
./rabbitmqadmin publish \
exchange=notifications \
routing_key=whatsapp \
payload='{"phone":"+911234567890","message":"Hello from RabbitMQ"}'
The message routes through the notifications exchange to the whatsapp.notifications queue, where the worker consumes it. In the first SSH terminal where the worker is running, you should see:
[Worker] Processing WhatsApp notification
[Worker] Recipient: +911234567890
[Worker] Message: Hello from RabbitMQ
[Worker] WhatsApp notification sent successfully
The complete worker output shows the full message flow:
[DEBUG] Connecting to RabbitMQ...
[DEBUG] Declaring queue...
[DEBUG] Setting QoS...
WhatsApp Worker started. Waiting for messages...
[DEBUG] Starting consumer loop (this should block)...
[Worker] Processing WhatsApp notification
[Worker] Recipient: +911234567890
[Worker] Message: Hello from RabbitMQ
[Worker] WhatsApp notification sent successfully
This output confirms that message routing works correctly through the direct exchange, the worker successfully consumes from the queue, manual acknowledgments are applied, and the end-to-end message flow is validated.
To confirm successful message consumption, check the queue status:
./rabbitmqadmin list queues name messages consumers
The output is similar to:
+------------------------+----------+-----------+
| name | messages | consumers |
+------------------------+----------+-----------+
| whatsapp.notifications | 0 | 1 |
+------------------------+----------+-----------+
The output shows zero messages remaining (all were consumed), one active consumer connected, and no message backlog in the queue.
When you’re done testing, press Ctrl+C in the worker terminal to exit the application.
You’ve implemented an asynchronous notification system using RabbitMQ with direct exchange routing, durable queues, manual acknowledgments, and fair dispatch. The Python worker processes WhatsApp notifications asynchronously, and the exact-match routing ensures messages go only to the intended queue.
This pattern works well for targeted notifications (email, SMS, WhatsApp, push notifications) where routing needs to be simple and predictable. Each notification type routes to a dedicated queue using an exact-match routing key, providing reliable, guaranteed delivery.
The key difference from Use Case 1 is the routing approach: Use Case 1 uses topic exchange with wildcard routing (order.*) for flexible event streams, while Use Case 2 uses direct exchange with exact routing (whatsapp) for targeted notifications.
When you’re finished, stop the RabbitMQ workers and delete the resources.
./rabbitmqadmin delete queue name=order.events
./rabbitmqadmin delete queue name=whatsapp.notifications
./rabbitmqadmin delete queue name=testqueue
./rabbitmqadmin delete exchange name=events
./rabbitmqadmin delete exchange name=notifications
When you are done, be sure to delete the Google Cloud VM and the firewall rule.