Multiple messaging systems exist, but they all serve the purpose of maintaining a stream of incoming messages and transferring them across your applications. A messaging system maintains a queue in its disks or memory, allowing it to store the messages producers add to the system while deleting messages that consumers have consumed/executed.
Messaging systems may follow one of two patterns: message queuing or a pub-sub pattern.
Countless solutions exist that were originally designed to act as message queuing systems, like RabbitMQ, RocketMQ, Apache ActiveMQ, Amazon SQS, and IBM MQ. Other technologies, like Apache Kafka, were designed to support pub-sub patterns. However, newer solutions like IronMQ meet the needs of both. So, let's explore message queue vs pub-sub systems side by side so you can decide which pattern best suits your environment.
IronMQ offers the option of both queuing and publish-subscribe so you can set up the perfect message broker. Book a free demo to get started.
Message queues are especially beneficial when you have applications that perform heavy lifting tasks, and you need to manage how much load they handle. Here are a few instances where a message queue comes in very handy:
Concurrency: If your software needs to handle a lot of simultaneous users or requests, a message queue can help manage this without overloading your servers.
Delayable Tasks: If there are certain tasks in your application that are not time-sensitive—such as sending emails or processing images—you can put them in a message queue to be carried out later, dedicating your real-time resources to other, more time-sensitive tasks.
Resilience: If your application needs to be resilient to peaks of traffic or temporary outages, you can use a message queue to buffer messages during periods of heavy load or server downtime.
Sending a message using RabbitMQ in Python is straightforward with the library pika
.
import
pika
# establish a connection
connection = pika.BlockingConnection(pika.ConnectionParameters(
'localhost'
))
channel = connection.channel()# create a queue
channel.queue_declare(queue=
'test_queue'
)
# send a message
channel.basic_publish(exchange=
''
,
routing_key=
'test_queue'
,
body=
'Hello World!'
)
(
" [x] Sent 'Hello World!'"
)
# close the connection
connection.close()
In this example, Python sends a simple "Hello World!" message into the test_queue
queue.
Consuming a message, or receiving it, from a queue in RabbitMQ using Python is also done easily with the pika
library. The following example demonstrates how to set up a simple consumer that waits for messages from the test_queue
queue and prints them out.
import
pika
# Establish a connection
connection = pika.BlockingConnection(pika.ConnectionParameters(
'localhost'
))
channel = connection.channel()# Ensure the queue exists by declaring it
channel.queue_declare(queue=
'test_queue'
)
# Define a callback function for processing messages
def
callback
(
ch, method, properties, body
):
(
f" [x] Received
{body}
"
)
# Set up subscription on the queue
channel.basic_consume(queue=
'test_queue'
,
on_message_callback=callback,auto_ack=
True
)
(
' [*] Waiting for messages. To exit press CTRL+C'
)
try
:
# Start consuming messages
channel.start_consuming()
except
KeyboardInterrupt:
(
' [!] Interrupted by user'
)
# Close the connection
connection.close()
In this example, the basic_consume
method is used to tell RabbitMQ that this code should receive messages from test_queue
. The callback
function is defined to handle messages that arrive from the queue. Whenever a message is received, RabbitMQ executes this callback function, passing the message's content. The auto_ack=True
parameter acknowledges the receipt of the message automatically, meaning you don't have to send a separate acknowledgment. This setup keeps the consumer running indefinitely, waiting for messages and printing them as they arrive, until it is interrupted by the user (e.g., via CTRL+C).
In a messaging system, message contracts play the role of defining a common language between different applications. They're usually formed with the following ingredients:
Data: The actual payload or information that's being communicated. In our example above, it's ‘Hello world!’.
Identifier: A unique identifier for each message. Often, this is automatically generated.
Metadata: Extra data about the message, such as the timestamp or message priority.
Message contracts are important because they ensure that both the producer and consumer 'speak the same language'. Without a clear contract, it would be hard for different parts of a distributed system to communicate effectively.