Development of a robust application, be it message publisher or message consumer, involves dealing with multiple kinds of failures: protocol exceptions, network failures, broker failures and so on. Correct error handling and recovery is not easy. This guide explains how the library helps you in dealing with issues like
as well as
This work is licensed under a Creative Commons Attribution 3.0 Unported License (including images and stylesheets). The source is available on Github.
This guide covers Bunny 2.10.x and later versions.
Here is the break-down of exceptions that can be raised by Bunny:
StandardError
Bunny::Exception
Bunny::ChannelAlreadyClosed
Bunny::ChannelLevelException
Bunny::AccessRefused
Bunny::ForcedChannelCloseError
Bunny::NotFound
Bunny::PreconditionFailed
Bunny::ResourceLocked
Bunny::ConnectionClosedError
Bunny::ConnectionLevelException
Bunny::ChannelError
Bunny::CommandInvalid
Bunny::ConnectionForced
Bunny::ForcedConnectionCloseError
Bunny::FrameError
Bunny::InternalError
Bunny::ResourceError
Bunny::UnexpectedFrame
Bunny::InconsistentDataError
Bunny::BadLengthError
Bunny::NoFinalOctetError
Bunny::NetworkFailure
Bunny::NotAllowedError
Bunny::PossibleAuthenticationFailureError
Bunny::AuthenticationFailureError
Bunny::ShutdownSignal
Bunny::TCPConnectionFailed
Timeout::Error
Bunny::ClientTimeout
Bunny::ConnectionTimeout
The rest of the document describes the most common ones. See Bunny exception definitions for more details.
When applications connect to the broker, they need to handle
connection failures. Networks are not 100% reliable, even with modern
system configuration tools like Chef or Puppet misconfigurations
happen and the broker might also be down. Error detection should
happen as early as possible. To handle TCP
connection failure, catch the Bunny::TCPConnectionFailure
exception:
begin
conn = Bunny.new("amqp://guest:guest@aksjhdkajshdkj.example82737.com")
conn.start
rescue Bunny::TCPConnectionFailed => e
puts "Connection to aksjhdkajshdkj.example82737.com failed"
end
Bunny::Session#start
will raise Bunny::TCPConnectionFailed
if a
connection fails. Code that catches it can write to a log about the
issue or use retry to execute the begin block one more time. Because
initial connection failures are due to misconfiguration or network
outage, reconnection to the same endpoint (hostname, port, vhost
combination) may result in the same issue over and over.
Another reason why a connection may fail is authentication failure. Handling authentication failure is very similar to handling initial TCP connection failure:
begin
conn = Bunny.new("amqp://guest8we78w7e8:guest2378278@127.0.0.1")
conn.start
rescue Bunny::PossibleAuthenticationFailureError => e
puts "Could not authenticate as #{conn.username}"
end
In case you are wondering why the exception name has "possible" in it: AMQP 0.9.1 spec requires broker implementations to simply close TCP connection without sending any more data when an exception (such as authentication failure) occurs before AMQP connection is open. In practice, however, when broker closes TCP connection between successful TCP connection and before AMQP connection is open, it means that authentication has failed.
RabbitMQ 3.2 introduces authentication failure notifications
which Bunny supports. When connecting to RabbitMQ 3.2 or later, Bunny will
raise Bunny::AuthenticationFailureError
when it receives a proper
authentication failure notification.
Detecting network connections is nearly useless if an application cannot recover from them. Recovery is the hard part in "error handling and recovery". Fortunately, the recovery process for many applications follows one simple scheme that Bunny can perform automatically for you.
When Bunny detects TCP connection failure, it will try to reconnect every 5 seconds. Currently there is no limit on the number of reconnection attempts.
To disable automatic connection recovery, pass :automatic_recovery => false
to Bunny.new
.
connection.close
Server-initiated connection.close
(issued due to an unrecoverable client
issue or when a connection is forced to close via RabbitMQ management UI/HTTP API
or when a server is shutting down)will result in an exception on the thread
Bunny::Session
was instantiated.
Bunny can be instructed from such exceptions (see Automatic Recovery below).
Many applications use the same recovery strategy that consists of the following steps:
Bunny provides a feature known as "automatic recovery" that performs these steps after connection recovery, while taking care of some of the more tricky details such as recovery of server-named queues with consumers.
Currently the topology recovery strategy is not configurable.
When automatic recovery is disabled, Bunny will raise
exceptions on the thread Bunny::Session
was instantiated on.
Bunny will recover from server-sent connection.close
, if you don't want it to do
so then pass recover_from_connection_close: false
to Bunny.new
.
Channel-level exceptions are more common than connection-level ones and often indicate issues applications can recover from (such as consuming from or trying to delete a queue that does not exist).
With Bunny, channel-level exceptions are raised as Ruby exceptions, for example,
Bunny::NotFound
, that provide access to the underlying channel.close
method
information:
begin
ch.queue_delete("queue_that_should_not_exist#{rand}")
rescue Bunny::NotFound => e
puts "Channel-level exception! Code: #{e.channel_close.reply_code}, message: #{e.channel_close.reply_text}"
end
begin
ch2 = conn.create_channel
q = "bunny.examples.recovery.q#{rand}"
ch2.queue_declare(q, :durable => false)
ch2.queue_declare(q, :durable => true)
rescue Bunny::PreconditionFailed => e
puts "Channel-level exception! Code: #{e.channel_close.reply_code}, message: #{e.channel_close.reply_text}"
ensure
conn.create_channel.queue_delete(q)
end
A few channel-level exceptions are common and deserve more attention.
The documentation is organized as a number of guides, covering various topics.
We recommend that you read the following guides first, if possible, in this order:
Please take a moment to tell us what you think about this guide on Twitter or the Bunny mailing list
Let us know what was unclear or what has not been covered. Maybe you do not like the guide style or grammar or discover spelling mistakes. Reader feedback is key to making the documentation better.