Introduction
RabbitMQ is fast, but performance can go down if it is not configured well.
Two key metrics are:
- Throughput: how many messages per second RabbitMQ can handle.
- Latency: how long it takes for a message to move from producer to consumer.
This article explains simple ways to tune RabbitMQ for better throughput and lower latency. This article is a continuation of my earlier posts on High Availability in RabbitMQ: Clustering and Mirrored Queues Explained and Scaling Microservices with RabbitMQ: Patterns and Best Practices. Together, they give a broader view of RabbitMQ performance, reliability, and scaling patterns.
Hardware and System Settings
- Use SSD storage
- Faster disk = faster persistence for durable queues.
- Increase memory
- RabbitMQ keeps messages in RAM as much as possible. More RAM = faster.
- Tune file descriptors
- RabbitMQ needs many open file handles for connections.
- Increase
ulimit -nto something like65535.
- Use multiple cores
- RabbitMQ is multi-threaded. More CPU cores = more parallel handling.
RabbitMQ Configuration
a. Prefetch Count (QoS)
- Controls how many messages a consumer can receive before ack.
- Low value = fairness but slower.
- Higher value = more throughput, but risk of overload.
Example in Go:
ch.Qos(50, 0, false) // allow 50 unacked messages per consumer
Publisher Confirms vs Transactions
RabbitMQ supports two ways to make sure published messages are safely stored:
- Transactions
- Similar to database transactions.
- Producer starts a transaction, publishes messages, and commits.
- If commit fails, messages are rolled back.
- Very reliable but slow, because it blocks.
ch.Tx() // start transaction
err = ch.Publish("", "task_queue", false, false, amqp.Publishing{
ContentType: "text/plain",
Body: []byte("Hello Transaction!"),
})
if err != nil {
ch.TxRollback()
log.Println("Transaction rolled back")
} else {
ch.TxCommit()
log.Println("Transaction committed")
}
- Publisher Confirms
- More modern and faster.
- Enable confirms with
ch.Confirm(false). - Broker sends ACK/NACK after message is stored.
- Works asynchronously, much lighter than transactions.
ch.Confirm(false) // enable confirms
ackChan := ch.NotifyPublish(make(chan amqp.Confirmation, 1))
err = ch.Publish("", "task_queue", false, false, amqp.Publishing{
ContentType: "text/plain",
Body: []byte("Hello Confirm!"),
})
if err != nil {
log.Fatal("Publish failed:", err)
}
confirm := <-ackChan
if confirm.Ack {
log.Println("Message confirmed by broker")
} else {
log.Println("Message not confirmed (NACK)")
}
Comparison
| Feature | Transactions (Tx) | Publisher Confirms |
|---|---|---|
| Reliability | Strong | Strong |
| Speed | Slow | Fast |
| Behavior | Blocking | Async |
| Best Use Case | Rare | Recommended |
Connection Management
- Reuse channels instead of creating too many.
- Avoid opening thousands of short-lived connections.
- Use a connection pool for efficiency.
Category: RabbitMQ
