These are notes for myself. I keep having to look these things up, so here they are.
Acknowledgement modes
Always use manual ack in production. Auto-ack drops messages if your consumer crashes mid-processing. The broker considers a message delivered the moment it leaves the queue, not when you’ve finished with it.
msgs, err := ch.Consume(
q.Name,
"", // consumer tag
false, // autoAck — always false
false, false, false, nil,
)
for d := range msgs {
if err := process(d.Body); err != nil {
d.Nack(false, true) // requeue on failure
continue
}
d.Ack(false)
}
The extra two lines are worth it.
Prefetch count
Set prefetch count to 1 unless you have a good reason not to.
err = ch.Qos(
1, // prefetch count
0, // prefetch size (0 = no limit)
false, // global
)
Without this, RabbitMQ will push as many messages as the consumer will accept. A slow consumer will hoard messages while fast consumers starve. Prefetch 1 means a consumer only gets the next message once it’s acked the previous one, so work distributes evenly.
Graceful shutdown
Don’t just os.Exit(0). Listen for SIGTERM, stop consuming, drain in-flight messages, then close the connection.
sigs := make(chan os.Signal, 1)
signal.Notify(sigs, syscall.SIGTERM, syscall.SIGINT)
go func() {
<-sigs
ch.Cancel(consumerTag, false) // stop new deliveries
// msgs channel will close once in-flight messages drain
}()
for d := range msgs {
process(d.Body)
d.Ack(false)
}
// clean exit
conn.Close()
The Cancel call tells RabbitMQ to stop sending messages to this consumer. The msgs channel closes once the in-flight messages are processed. Clean.