Learning Notes #5 β Message Queues | RabbitMQ
Github: https://github.com/syedjaferk/rabbitmq_message_queues
Imagine you own a busy online store. Customers place orders, payments are processed, inventory is updated, and confirmation emails are sent.
If these steps happen one after another in real-time (synchronous), your website could slow down or even crash under high demand. This is where message queues come in to picture. They help different parts of your system communicate smoothly and handle tasks efficiently, even during a rush. Its one of the solution for asynchronous communication.
What is a Message Queue?
A message queue is a software system that enables different parts of an application to send and receive messages asynchronously. Messages are temporarily stored in a queue until the recipient is ready to process them.
For example, think of it as a waiting line at a busy coffee shop. Each order (or message) waits in line until itβs picked up and handled by a coffee maker (or worker). The beauty of a message queue is that the coffee shop (producer) can keep taking orders without waiting for the coffee maker (consumer) to finish the current one.
Hereβs how it works:
- The producer sends messages to the queue.
- The queue stores the messages.
- The consumer picks up messages one by one to process them.
RabbitMQ is one kind of tool which helps in enabling async communication.
Key Components of RabbitMQ (a Popular Message Queue System)
- Producer: The sender of messages. For example, your website sending an order to the queue.
- Queue: The holding area for messages, like a to-do list. Each order waits here until processed.
- Consumer: The worker that processes messages. For example, the service that charges a credit card.
- Exchange: Think of this as a traffic controller. It decides which queue gets each message based on rules you set.
- Message: The data being sent, such as order details (customer name, items, total price).
- Acknowledgements (ACKs): A signal from the consumer to RabbitMQ saying, βMessage processed successfully!β.
How a Message Queue Solves Real Problems
Scenario: Imagine your online store uses a message queue during a holiday rush.
- Placing Orders
- Customers place orders on your website (producer).
- Orders are sent to the RabbitMQ queue.
- Processing Payments
- The payment service (consumer) picks up orders from the queue, one by one, to charge credit cards.
- Sending Emails
- Once payment is successful, another consumer sends confirmation emails.
- Updating Inventory
- A third consumer updates the inventory system.
Without a queue: All these tasks would happen one after the other, causing delays and potential failures.
With a queue: Each task works independently and efficiently, ensuring smooth operations.
Simple RabbitMQ Example
Step1: I am spinning up a RabbitMQ from a Docker
docker run -it --rm --name rabbitmq -p 5672:5672 -p 15672:15672 rabbitmq:4.0-management
Step 2: Producer Code (Sending Messages)
import pika connection = pika.BlockingConnection(pika.ConnectionParameters('localhost')) channel = connection.channel() channel.queue_declare(queue='order_queue') channel.basic_publish(exchange='', routing_key='order_queue', body='Order #12345') print("[x] Sent 'Order #12345'") connection.close()
Explanation
pika.ConnectionParameters('localhost')
: Connects to RabbitMQ running locally.channel.queue_declare(queue='order_queue')
: Ensures the queue exists. If it doesnβt, RabbitMQ will create it.channel.basic_publish(...)
: Publishes a message (in this case, βOrder #12345β) to the specified queue.connection.close()
: Cleans up and closes the connection.
Step 3: Consumer Code (Processing Message)
import pika connection = pika.BlockingConnection(pika.ConnectionParameters('localhost')) channel = connection.channel() channel.queue_declare(queue='order_queue') def callback(ch, method, properties, body): print(f"[x] Processed {body}") ch.basic_ack(delivery_tag=method.delivery_tag) channel.basic_consume(queue='order_queue', on_message_callback=callback) print(' [*] Waiting for messages. To exit press CTRL+C') channel.start_consuming()
Explanation
channel.queue_declare(queue='order_queue')
: Ensures the consumer is listening to the correct queue.callback
: A function that processes each message. Here, it prints the message content and acknowledges it.channel.basic_consume(...)
: Binds the callback function to the queue, so the consumer processes messages as they arrive.channel.start_consuming()
: Starts the consumer, waiting for messages indefinitely.
Best Practices (Not Tried β Just Got it from Course page.)
- Keep Messages Small: Only send necessary data to avoid delays.
- Use Dead Letter Queues: Handle failed messages separately to keep the main queue clear.
- Monitor Performance: Watch queue sizes and processing times to prevent backlogs.
- Scale Consumers: Add more workers during busy times to process messages faster.
- Secure Your System: Use encryption and authentication to protect sensitive data.