Library / SDK
wagslane/go-rabbitmq avatar
wagslane/go-rabbitmq

go-rabbitmq: a RabbitMQ client that reconnects on its own

A wrapper of streadway/amqp that provides reconnection logic and sane defaults

1,002 stars150 forksGoMIT

At a glance

What is it?
go-rabbitmq wraps the rabbitmq/amqp091-go client with automatic reconnection, a handler-based consumer, flow control and TCP block handling, and it deliberately keeps exactly one dependency. It is MIT licensed, the current release is v0.16.0 from 2026-07-12, and the API is still in v0 with small breaking changes possible before v1.
Who is it for?
go-rabbitmq fits a Go service that consumes from RabbitMQ and cannot afford to lose its subscription on a broker restart, and it fits a team that wants the convenience without a second AMQP library.
Can I use it commercially?
Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 102 days ago.
What is it written in?
Mainly Go, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 23, 2026, and from our analysis. They are not legal advice.

Editorial analysis

One dependency, and a deliberate narrowing of the protocol surface

go-rabbitmq is a wrapper of the rabbitmq/amqp091-go library that provides reconnection logic and sane defaults, and the author is clear about the relationship.

The motivation section recommends starting with the underlying library. Streadway's AMQP library is described as currently the most robust and well-supported Go client, a fantastic option, and something to start with and see if it fulfills your needs. The reason it does not is that its project has made an effort to stay within the scope of the AMQP protocol, and as such, no reconnection logic and few ease-of-use abstractions are provided.

That is the whole argument. A protocol-faithful client will not reconnect, because reconnection is not in the AMQP specification, it is a RabbitMQ behaviour. A client that stayed inside the spec would leave every application to implement reconnection itself, and every application would implement it slightly differently and slightly wrongly.

The goal is stated as providing most, but not all, of the nitty-gritty functionality of the underlying library, while making it easier to work with through a higher-level API, and it is built specifically for Rabbit rather than for the AMQP protocol. The five things it wants are automatic reconnection, multithreaded consumers via a handler function, reasonable defaults, flow control handling and TCP block handling.

The dependency policy is enforced in the manifest. The goal is to keep dependencies limited to one, and `go.mod` reflects that exactly, with a single require, `github.com/rabbitmq/amqp091-go v1.12.0`. A library that most applications will pull in transitively is worth keeping small, and the `vendor/` directory in the repository is consistent with that intent.

It is MIT licensed, the last push was on 2026-07-13 and the release is v0.16.0 from 2026-07-12.

The consumer is a handler function, and the action decides the fate of the message

The consumer API is the part of the library that most changes how you write a consumer, and the quick start example is short enough to read in full.

go
conn, err := rabbitmq.NewConn(
	"amqp://guest:guest@localhost",
	rabbitmq.WithConnectionOptionsLogging,
)
if err != nil {
	log.Fatal(err)
}
defer conn.Close()

consumer, err := rabbitmq.NewConsumer(
	conn,
	"my_queue",
	rabbitmq.WithConsumerOptionsRoutingKey("my_routing_key"),
	rabbitmq.WithConsumerOptionsExchangeName("events"),
	rabbitmq.WithConsumerOptionsExchangeDeclare,
)
if err != nil {
	log.Fatal(err)
}
defer consumer.Close()

err = consumer.Run(func(d rabbitmq.Delivery) rabbitmq.Action {
	log.Printf("consumed: %v", string(d.Body))
	// rabbitmq.Ack, rabbitmq.NackDiscard, rabbitmq.NackRequeue
	return rabbitmq.Ack
})

Three things are worth pulling out. The handler returns a `rabbitmq.Action` rather than a bare error, so the acknowledgement decision is part of the return value: `Ack`, `NackDiscard` or `NackRequeue`. The handler is called on its own goroutine, which is what the multithreaded consumer goal means in practice, and it is why the reconnection logic can be layered underneath without the handler knowing. And the options are trailing variadic arguments, so the mandatory arguments are the connection and the queue name, and everything else is opt-in.

The `WithConsumerOptionsExchangeDeclare` option is there for a reason. The documentation is explicit that the queue will be declared automatically, but the exchange will not, and you will probably want to bind to at least one routing key. So the consumer declares its queue, and you have to opt in to the exchange and the binding.

The publisher side mirrors it. The exchange is not declared by default, which is why the recommended options include `WithPublisherOptionsExchangeName("events")` and `WithPublisherOptionsExchangeDeclare`, and the publish call takes the body, the routing key and then options such as `WithPublishOptionsContentType("application/json")` and `WithPublishOptionsExchange("events")`.

Defaults, and the one option that will surprise you

The options and configuring section states the defaults, and the pattern in them is that the library is cautious about creating things on the broker.

By default, queues are declared if they did not already exist by new consumers. By default, routing-key bindings are declared by consumers if you are using `WithConsumerOptionsRoutingKey`. And by default, exchanges are not declared by publishers or consumers if they do not already exist, hence `WithPublisherOptionsExchangeDeclare` and `WithConsumerOptionsExchangeDeclare`.

The asymmetry between queues and exchanges is a considered choice rather than an oversight. A queue is a consumer's own business, and the consumer is the thing that needs it to exist. An exchange is shared topology that other publishers and consumers also bind to, and a component silently creating it can change the routing behaviour of a whole application. If two services both declare the same exchange with different parameters, the second declaration fails or, worse, the first one wins and the second service's bindings are wrong. So the library makes you opt in.

The author also says the options are numerous, that you should read up on all of them in the GoDoc, and that he tries to pick sane and simple defaults. That combination, a large option surface with sensible defaults, is the shape of a library that has been used in production by enough people to know which knobs matter.

The examples directory reinforces it, with directories for cluster, consumer, logger, multiconsumer, multipublisher, publisher and publisher_confirm. The multi-consumer and multi-publisher directories are the ones to look at if you are wondering how the thread-per-handler model composes, and publisher_confirm is the one for the case where you need the broker to acknowledge receipt rather than just accept the write.

Closing order, and what a v0 API means for you

Two short sections carry the operational warnings, and both are the kind of thing that is easy to skip and expensive to get wrong.

On closing and resources: close your publishers and consumers when you are done with them and do not attempt to reuse them. Only close the connection itself once you have closed all associated publishers and consumers. The ordering is not a style preference. A consumer that is closed while a handler is still running, or a connection closed while a publisher is still registered, is a use-after-free in the making, and the library is asking you to sequence it correctly rather than defending against your mistakes.

On stability: the API is currently in v0. There are no plans for huge changes, but there may be some small breaking changes before v1. That is an honest statement, and the practical reading is that a minor version bump can move an option or change a signature, so a project that depends on this library should pin the version and read the changelog on upgrade rather than tracking the main branch.

The integration testing note describes how the harder behaviour gets checked. By setting `ENABLE_DOCKER_INTEGRATION_TESTS=TRUE` during `go test -v ./...`, the integration tests run, and they launch a rabbitmq container in the local Docker daemon and test some publish and consume actions. So the reconnection and flow control behaviour is verified against a real broker rather than a mock, which for a library whose main selling point is what happens when the connection drops is exactly the right thing to test.

The `Makefile` shows the local checks, with `test` running the suite under the race detector, `vet` and `staticcheck` as separate targets, and `all` running all three, which is the standard Go hygiene for a library where the concurrency is the interesting part.

Where go-rabbitmq is the wrong choice

The first case is a project that needs the full protocol. The author says the goal is most, but not all, of the underlying library's functionality, and the library is built for Rabbit specifically rather than for AMQP. If you need an AMQP 1.0 broker, or a feature RabbitMQ has but this wrapper does not expose, the honest move is to use amqp091-go directly, which the author recommends as a starting point anyway.

The second case is a team that has decided the protocol-faithful approach is the right one. A library that reconnects automatically makes a decision on your behalf about what happens to in-flight messages, and in a system where a broker restart should stop the application rather than silently retry, that automatic behaviour is wrong. The wrapper is a convenience and it is a policy, and some systems want neither.

The third is a project that already has a messaging layer. If you have an internal abstraction over RabbitMQ, Kafka and a cloud queue, wrapping the wrapper just moves the problem one layer down, and the reconnection behaviour has to be reconciled with whatever your abstraction already does about connection lifetime.

The fourth is a long-lived production codebase that needs a stable API. The library is in v0 by its own admission, and it depends on a specific version of amqp091-go, so an upgrade path means tracking two version numbers.

A note on the flow control and TCP block handling, since they are the features that are hardest to get right and the library does not document their behaviour in the readme. The `publish_flow_block.go` file in the source is where that logic lives, and if your workload involves a slow consumer and a fast producer, that is the code to read before you rely on the defaults, because the default behaviour when the broker blocks the publisher is exactly the kind of thing that differs between a small deployment and a large one.

Editorial conclusion

go-rabbitmq fits a Go service that consumes from RabbitMQ and cannot afford to lose its subscription on a broker restart, and it fits a team that wants the convenience without a second AMQP library. It does not fit a project that needs the full AMQP protocol surface, because the author says the goal is most but not all of the underlying library's functionality, and it does not fit a team that needs a stable API today, since the library is in v0 and says small breaking changes are still possible. Before you adopt it, read the closing and resources section, because the lifetime rules around publishers, consumers and the connection are stricter than they look.

Frequently asked questions

What does go-rabbitmq add to amqp091-go?

Automatic reconnection, multithreaded consumers via a handler function, reasonable defaults, flow control handling and TCP block handling, through a higher-level API. It is built specifically for RabbitMQ rather than for the AMQP protocol, and it keeps a single dependency.

How do I install go-rabbitmq?

Inside a Go module, run `go get github.com/wagslane/go-rabbitmq`. The module requires Go 1.26.0 and depends only on `github.com/rabbitmq/amqp091-go`.

Does go-rabbitmq declare queues and exchanges automatically?

Queues are declared by default if they do not already exist. Exchanges are not declared by default, so you need `WithPublisherOptionsExchangeDeclare` or `WithConsumerOptionsExchangeDeclare`, and routing-key bindings are declared by consumers when you use `WithConsumerOptionsRoutingKey`.

How do I run the go-rabbitmq integration tests?

Set `ENABLE_DOCKER_INTEGRATION_TESTS=TRUE` during `go test -v ./...`. The integration tests launch a RabbitMQ container in the local Docker daemon and test publish and consume actions, so the reconnection and flow control behaviour is checked against a real broker.

Official sources

  1. License: MIT
  2. Project website
  3. README
  4. Releases
  5. wagslane/go-rabbitmq on GitHub
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/wagslane-go-rabbitmq.svg)](https://hysenlabs.com/projects/wagslane-go-rabbitmq)

Was this useful?