Time to write your first code with RabbitMQ. In this episode you create a simple producer and consumer using Python pika, Node.js amqplib, and Go amqp091-go, then learn the best practices for managing connections, channels, heartbeats, and graceful shutdown.

This is the happiest moment in this series: you will write your first program that truly communicates through RabbitMQ. We start with the simplest pattern — one producer sending a "Hello World" message to a queue, and one consumer printing it out.
Even though it's simple, this episode contains important foundations that will not change throughout your career: how to open a connection, how to create a channel, how to publish a message, and how to process incoming messages. Mistakes in connection and channel management at this stage will become serious problems in production.
We show implementations in the three most popular languages — Python with pika, Node.js with amqplib, and Go with amqp091-go. Pick the language you're most comfortable with, but try to understand the common pattern: they all share an identical flow.
Install the client library in your language of choice. For Python:
pip install pikaThen write the producer. The producer opens a connection, creates a channel, declares the queue, and publishes a message:
import pika
connection = pika.BlockingConnection(pika.ConnectionParameters("localhost"))
channel = connection.channel()
channel.queue_declare(queue="hello")
channel.basic_publish(exchange="", routing_key="hello", body=b"Hello World")
print("pesan terkirim")
connection.close()Notice that here routing_key plays the role of the queue name, because we use the default exchange (an empty exchange). Exchange details will be covered in episode 6.
The consumer uses a callback that is invoked every time a message arrives:
import pika
connection = pika.BlockingConnection(pika.ConnectionParameters("localhost"))
channel = connection.channel()
channel.queue_declare(queue="hello")
def callback(ch, method, properties, body):
print(f"menerima: {body}")
channel.basic_consume(queue="hello", on_message_callback=callback, auto_ack=True)
print("menunggu pesan...")
channel.start_consuming()The channel.start_consuming function blocks the program and keeps listening for messages. auto_ack=True tells the broker that the message is considered processed immediately without a manual ack.
For Node.js, install amqplib then write the producer:
npm install amqplibconst amqp = require("amqplib");
async function main() {
const conn = await amqp.connect("amqp://localhost");
const ch = await conn.createChannel();
await ch.assertQueue("hello");
ch.sendToQueue("hello", Buffer.from("Hello World"));
console.log("pesan terkirim");
await conn.close();
}
main();amqp.connect returns a promise, and assertQueue declares the queue if it doesn't exist yet. In Node.js, we'll improve the async pattern above with connection recovery in episode 28.
For Go, install the official package amqp091-go:
go get github.com/rabbitmq/amqp091-gopackage main
import (
"log"
amqp "github.com/rabbitmq/amqp091-go"
)
func main() {
conn, err := amqp.Dial("amqp://localhost")
if err != nil {
log.Fatal(err)
}
defer conn.Close()
ch, err := conn.Channel()
if err != nil {
log.Fatal(err)
}
defer ch.Close()
err = ch.Publish("", "hello", false, false, amqp.Publishing{Body: []byte("Hello World")})
if err != nil {
log.Fatal(err)
}
log.Println("pesan terkirim")
}Notice that Publish in Go does not declare the queue automatically — the declaration must be done explicitly. This is a reminder that declaring queues is the application's responsibility, not the broker's.
One thing to always remember: a connection is an expensive resource, a channel is a cheap resource. An application should use one or a few shared connections, and create a new channel for each concurrent task. In Python, if you use BlockingConnection in many threads, create one connection per thread. In Node.js and Go, a single connection can be used concurrently because its channels are thread-safe.
Every connection must set up a heartbeat — a periodic ping so the broker knows the client is still alive. The RabbitMQ default heartbeat is 60 seconds. If the client doesn't send data for too long, the broker will disconnect what it considers a dead connection.
For graceful shutdown, the consumer must stop processing new messages, then close the channel and connection in the right order:
channel.stop_consuming()
channel.close()
connection.close()connection.close() must be called after all channels are closed, so the broker releases resources cleanly.
Warning
Never close the connection inside a consumer callback without handling the message currently being processed. The message could be lost or acked twice. Always think through the shutdown order in episode 5 when we discuss acknowledgment.
In episode 4 you wrote your first producer and consumer in Python, Node.js, and Go; understood the connection-channel-publish-consume flow; and learned best practices for connection management, heartbeats, and clean shutdown.
Key takeaways:
auto_ack=True makes demos easier, but is dangerous for production.In the next episode we will build work queues — distributing heavy tasks to many workers with round-robin, using manual acknowledgment so messages aren't lost when a worker crashes, and setting prefetch with QoS so each worker works within its capacity. This concept is the foundation of every task processing system in the real world!