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.