amqp 0.8.0.rc13 → 0.8.0.rc14
Sign up to get free protection for your applications and to get access to all the features.
- data/.rspec +2 -1
- data/.travis.yml +8 -2
- data/.yardopts +1 -0
- data/CHANGELOG +9 -0
- data/Gemfile +17 -11
- data/README.md +26 -16
- data/amqp.gemspec +2 -2
- data/bin/ci/before_build.sh +21 -0
- data/docs/08Migration.textile +199 -5
- data/docs/AMQP091ModelExplained.textile +322 -0
- data/docs/Bindings.textile +24 -4
- data/docs/Clustering.textile +1 -1
- data/docs/ConnectingToTheBroker.textile +98 -82
- data/docs/ConnectionEncryptionWithTLS.textile +65 -5
- data/docs/DocumentationGuidesIndex.textile +93 -13
- data/docs/Durability.textile +1 -1
- data/docs/ErrorHandling.textile +458 -94
- data/docs/Exchanges.textile +901 -87
- data/docs/GettingStarted.textile +278 -143
- data/docs/PatternsAndUseCases.textile +420 -0
- data/docs/Queues.textile +730 -178
- data/docs/RabbitMQVersions.textile +18 -3
- data/docs/RunningTests.textile +1 -1
- data/docs/TestingWithEventedSpec.textile +121 -0
- data/docs/Troubleshooting.textile +15 -1
- data/docs/VendorSpecificExtensions.textile +1 -1
- data/docs/diagrams/001_hello_world_example_routing.png +0 -0
- data/docs/diagrams/002_blabbr_example_routing.png +0 -0
- data/docs/diagrams/003_weathr_example_routing.png +0 -0
- data/docs/diagrams/004_fanout_exchange.png +0 -0
- data/docs/diagrams/005_direct_exchange.png +0 -0
- data/docs/diagrams/redhat/direct_exchange.png +0 -0
- data/docs/diagrams/redhat/fanout_exchange.png +0 -0
- data/docs/diagrams/redhat/topic_exchange.png +0 -0
- data/examples/error_handling/automatic_recovery_of_channel_and_queues.rb +50 -0
- data/examples/error_handling/automatically_recovering_hello_world_consumer.rb +51 -0
- data/examples/error_handling/automatically_recovering_hello_world_consumer_that_uses_a_server_named_queue.rb +51 -0
- data/examples/error_handling/basic_connection_failover.rb +22 -0
- data/examples/error_handling/channel_level_exception.rb +9 -2
- data/examples/error_handling/connection_level_exception.rb +8 -1
- data/examples/error_handling/connection_level_exception_with_objects.rb +49 -0
- data/examples/error_handling/connection_loss_handler.rb +1 -5
- data/examples/error_handling/hello_world_producer.rb +43 -0
- data/examples/error_handling/insufficient_permissions.rb +54 -0
- data/examples/error_handling/manual_connection_and_channel_recovery.rb +71 -0
- data/examples/error_handling/queue_exclusivity_violation.rb +41 -0
- data/examples/error_handling/queue_name_violation.rb +31 -0
- data/examples/exchanges/autodeletion_of_exchanges.rb +1 -4
- data/examples/guides/queues/01a_declaring_a_server_named_queue_using_queue_constructor.rb +7 -8
- data/examples/guides/queues/01b_declaring_a_queue_using_queue_constructor.rb +7 -8
- data/examples/guides/queues/02a_declaring_a_durable_shared_queue.rb +5 -8
- data/examples/guides/queues/02b_declaring_a_durable_shared_queue.rb +5 -8
- data/examples/guides/queues/03a_declaring_a_temporary_exclusive_queue.rb +7 -8
- data/examples/guides/queues/04_bind_a_queue_using_exchange_instance.rb +9 -10
- data/examples/guides/queues/05_bind_a_queue_using_exchange_name.rb +8 -10
- data/examples/guides/queues/06_subscribe_to_receive_messages.rb +10 -12
- data/examples/guides/queues/07_fetch_a_message_from_the_queue.rb +14 -14
- data/examples/guides/queues/08_unsubscribing_a_consumer.rb +13 -16
- data/examples/guides/queues/09_unbinding_from_exchange.rb +16 -22
- data/examples/guides/queues/10_purge_a_queue.rb +13 -18
- data/examples/guides/queues/11_deleting_a_queue.rb +14 -19
- data/examples/guides/queues/12_objects_that_consume_messages.rb +69 -0
- data/examples/guides/queues/13_objects_that_consume_messages_take_two.rb +89 -0
- data/examples/hello_world.rb +1 -3
- data/examples/hello_world_with_an_empty_string.rb +5 -6
- data/examples/inspecting_server_information.rb +45 -0
- data/examples/issues/issue_93.rb +23 -0
- data/examples/issues/issue_94.rb +23 -0
- data/examples/patterns/command/consumer.rb +45 -0
- data/examples/patterns/command/producer.rb +26 -0
- data/examples/patterns/request_reply/client.rb +29 -0
- data/examples/patterns/request_reply/server.rb +26 -0
- data/examples/publishing/publishing_a_one_off_message.rb +6 -4
- data/examples/publishing/returned_messages.rb +2 -10
- data/examples/queues/accessing_message_metadata.rb +15 -13
- data/examples/queues/queue_status.rb +12 -15
- data/examples/routing/fanout_routing.rb +33 -0
- data/examples/routing/headers_routing.rb +17 -15
- data/examples/routing/round_robin_with_direct_exchange.rb +39 -0
- data/examples/routing/round_robin_with_the_default_exchange.rb +38 -0
- data/examples/routing/unroutable_mandatory_message_is_returned.rb +33 -0
- data/examples/routing/weather_updates.rb +15 -20
- data/examples/tls/using_tls.rb +41 -0
- data/lib/amqp/bit_set.rb +80 -0
- data/lib/amqp/broker.rb +72 -0
- data/lib/amqp/channel.rb +93 -13
- data/lib/amqp/client.rb +11 -22
- data/lib/amqp/compatibility/ruby187_patchlevel_check.rb +2 -0
- data/lib/amqp/connection.rb +2 -3
- data/lib/amqp/consumer.rb +208 -0
- data/lib/amqp/deprecated/fork.rb +2 -0
- data/lib/amqp/deprecated/mq.rb +2 -0
- data/lib/amqp/exchange.rb +6 -4
- data/lib/amqp/extensions/rabbitmq.rb +3 -1
- data/lib/amqp/header.rb +76 -14
- data/lib/amqp/int_allocator.rb +96 -0
- data/lib/amqp/logger.rb +2 -0
- data/lib/amqp/queue.rb +242 -86
- data/lib/amqp/rpc.rb +2 -0
- data/lib/amqp/session.rb +169 -9
- data/lib/amqp/utilities/event_loop_helper.rb +2 -0
- data/lib/amqp/utilities/server_type.rb +2 -0
- data/lib/amqp/version.rb +2 -2
- data/lib/mq.rb +4 -2
- data/lib/mq/logger.rb +3 -1
- data/lib/mq/rpc.rb +3 -1
- data/spec/integration/authentication_spec.rb +17 -10
- data/spec/integration/automatic_binding_for_default_direct_exchange_spec.rb +1 -1
- data/spec/integration/automatic_recovery_predicate_spec.rb +68 -0
- data/spec/integration/basic_get_spec.rb +2 -1
- data/spec/integration/{extensions/basic_return_spec.rb → basic_return_spec.rb} +2 -1
- data/spec/integration/channel_level_exception_handling_spec.rb +53 -0
- data/spec/integration/connection_level_exception_handling_spec.rb +49 -0
- data/spec/integration/declare_and_immediately_bind_a_server_named_queue_spec.rb +38 -17
- data/spec/integration/declare_one_hundred_server_named_queues_spec.rb +44 -0
- data/spec/integration/direct_exchange_routing_spec.rb +125 -0
- data/spec/integration/exchange_declaration_spec.rb +75 -46
- data/spec/integration/extensions/rabbitmq/publisher_confirmations_spec.rb +180 -0
- data/spec/integration/{workload_distribution_spec.rb → fanout_exchange_routing_spec.rb} +10 -9
- data/spec/integration/headers_exchange_routing_spec.rb +269 -0
- data/spec/integration/hello_world_spec.rb +77 -0
- data/spec/integration/immediate_messages_spec.rb +59 -0
- data/spec/integration/mandatory_messages_spec.rb +52 -0
- data/spec/integration/message_metadata_access_spec.rb +106 -0
- data/spec/integration/multiple_consumers_per_queue_spec.rb +319 -0
- data/spec/integration/ordering_of_published_messages_spec.rb +96 -0
- data/spec/integration/queue_declaration_spec.rb +8 -8
- data/spec/integration/queue_status_spec.rb +66 -0
- data/spec/integration/recovery/per_channel_automatic_recovery_on_graceful_broker_shutdown_spec.rb +76 -0
- data/spec/integration/recovery/per_channel_automatic_recovery_spec.rb +72 -0
- data/spec/integration/redelivery_of_unacknowledged_messages_spec.rb +96 -0
- data/spec/integration/regressions/concurrent_publishing_on_the_same_channel_spec.rb +91 -0
- data/spec/integration/regressions/empty_message_body_spec.rb +56 -0
- data/spec/integration/regressions/issue66_spec.rb +2 -1
- data/spec/integration/reply_queue_communication_spec.rb +2 -1
- data/spec/integration/store_and_forward_spec.rb +4 -3
- data/spec/integration/topic_subscription_spec.rb +2 -1
- data/spec/integration/tx_commit_spec.rb +124 -0
- data/spec/integration/tx_rollback_spec.rb +167 -0
- data/spec/spec_helper.rb +44 -71
- data/spec/unit/amqp/bit_set_spec.rb +127 -0
- data/spec/unit/amqp/channel_id_allocation_spec.rb +40 -0
- data/spec/unit/amqp/connection_spec.rb +4 -2
- data/spec/unit/amqp/int_allocator_spec.rb +116 -0
- metadata +92 -26
- data/CONTRIBUTORS +0 -29
- data/docs/Routing.textile +0 -30
- data/examples/real-world/task-queue/README.textile +0 -3
- data/examples/real-world/task-queue/consumer.rb +0 -27
- data/examples/real-world/task-queue/producer.rb +0 -22
- data/spec/unit/amqp/basic_spec.rb +0 -39
- data/tasks.rb +0 -4
@@ -1,11 +1,13 @@
|
|
1
1
|
# @title Ruby AMQP gem: Using TLS
|
2
2
|
|
3
|
-
h1.
|
3
|
+
h1. Using TLS with Ruby amqp gem
|
4
4
|
|
5
5
|
|
6
6
|
h2. About this guide
|
7
7
|
|
8
|
-
|
8
|
+
This guide covers connection to AMQP brokers using TLS (also known as SSL) and related issues. This guide
|
9
|
+
does not explain basic TLS concepts. For that, refer to resources like "Introduction to SSL":https://developer.mozilla.org/en/Introduction_to_SSL
|
10
|
+
or "Wikipedia page on TLS":http://en.wikipedia.org/wiki/Transport_Layer_Security.
|
9
11
|
|
10
12
|
|
11
13
|
h2. Covered versions
|
@@ -13,16 +15,74 @@ h2. Covered versions
|
|
13
15
|
This guide covers "Ruby amqp gem":http://github.com/ruby-amqp/amqp v0.8.0 and later.
|
14
16
|
|
15
17
|
|
18
|
+
h2. Broker version requirements
|
16
19
|
|
17
|
-
|
20
|
+
h3. RabbitMQ
|
18
21
|
|
19
|
-
|
22
|
+
RabbitMQ supports TLS since version 1.7.0. Minimum requirements are
|
23
|
+
|
24
|
+
* Erlang R13B
|
25
|
+
* Erlang SSL application 3.10
|
26
|
+
|
27
|
+
The recommended distribution is R14B (SSL 4.0.1) or later. This should be considered the minimum configuration for Java
|
28
|
+
and Erlang clients due to an incorrect RC4 implementation in earlier versions of Erlang.
|
29
|
+
|
30
|
+
Learn more at "rabbitmq.com TLS page":http://www.rabbitmq.com/ssl.html.
|
31
|
+
|
32
|
+
|
33
|
+
|
34
|
+
h2. Pre-requisites
|
35
|
+
|
36
|
+
AMQP brokers typically need to be configured to use TLS. Just like Web servers, TLS connections are usually accepted
|
37
|
+
on a separate port (5671). "rabbitmq.com TLS page":http://www.rabbitmq.com/ssl.html describes how to configure RabbitMQ
|
38
|
+
to use TLS, how to generate certificates for development and so on.
|
39
|
+
|
40
|
+
|
41
|
+
|
42
|
+
h2. Connecting to AMQP broker using TLS
|
43
|
+
|
44
|
+
To instruct Ruby amqp gem to use TLS for connection, pass :ssl option that specifies certificate chain file path
|
45
|
+
as well as private key file path:
|
46
|
+
|
47
|
+
<pre>
|
48
|
+
<code>
|
49
|
+
AMQP.start(:port => 5671,
|
50
|
+
:ssl => {
|
51
|
+
:cert_chain_file => certificate_chain_file_path,
|
52
|
+
:private_key_file => client_private_key_file_path
|
53
|
+
}) do |connection|
|
54
|
+
puts "Connected, authenticated. TLS seems to work."
|
55
|
+
|
56
|
+
connection.disconnect { puts "Now closing the connection…"; EventMachine.stop }
|
57
|
+
end
|
58
|
+
</code>
|
59
|
+
</pre>
|
60
|
+
|
61
|
+
Note that TLS connection may take a bit of time to establish (up to several seconds in some cases). To verify that
|
62
|
+
broker connection actually uses TLS, refer to RabbitMQ log file:
|
63
|
+
|
64
|
+
<pre>
|
65
|
+
=INFO REPORT==== 28-Jun-2011::08:41:24 ===
|
66
|
+
accepted TCP connection on 0.0.0.0:5671 from 127.0.0.1:53444
|
67
|
+
|
68
|
+
=INFO REPORT==== 28-Jun-2011::08:41:24 ===
|
69
|
+
starting TCP connection <0.9904.0> from 127.0.0.1:53444
|
70
|
+
|
71
|
+
=INFO REPORT==== 28-Jun-2011::08:41:24 ===
|
72
|
+
upgraded TCP connection <0.9904.0> to SSL
|
73
|
+
</pre>
|
74
|
+
|
75
|
+
|
76
|
+
|
77
|
+
h2. Example code
|
78
|
+
|
79
|
+
TLS example (as well as sample certificates you can use to get started with) can be found in the "amqp gem git repository":https://github.com/ruby-amqp/amqp/tree/master/examples
|
20
80
|
|
21
81
|
|
22
82
|
|
23
83
|
h2. Tell us what you think!
|
24
84
|
|
25
|
-
Please take a moment and tell us what you think about this guide on "Ruby AMQP mailing list":http://groups.google.com/group/ruby-amqp:
|
85
|
+
Please take a moment and tell us what you think about this guide "on Twitter":http://twitter.com/rubyamqp or "Ruby AMQP mailing list":http://groups.google.com/group/ruby-amqp:
|
26
86
|
what was unclear? what wasn't covered? maybe you don't like guide style or grammar and spelling are incorrect? Readers feedback is
|
27
87
|
key to making documentation better.
|
28
88
|
|
@@ -2,32 +2,112 @@
|
|
2
2
|
|
3
3
|
h1. Ruby AMQP gem documentation guides
|
4
4
|
|
5
|
-
h2.
|
5
|
+
h2. Which versions of the amqp gem do the guides cover?
|
6
6
|
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
7
|
+
These guides cover v0.8.0.RC13 and later of the "Ruby amqp gem":http://github.com/ruby-amqp/amqp.
|
8
|
+
|
9
|
+
|
10
|
+
h2. Documentation structure and how to read these guides
|
11
|
+
|
12
|
+
We recommend that you read the documentation guides in the order that they are listed. The *Getting Started* guide is written as a tutorial that
|
13
|
+
describes several typical applications and their problem scope, then provides full code examples and finally breaks them down
|
14
|
+
into smaller pieces that are explained in detail.
|
15
|
+
|
16
|
+
Once you are finished with the tutorial, reading the other guides in sequence will gradually explain all of the AMQP 0.9.1 features that are relevant to
|
17
|
+
application developers, including application design concerns and common practices. At present, some guides are yet to be written
|
18
|
+
but *most of the content is concentrated in just 3-4 guides* that are about 80% finished and provide plenty of examples.
|
19
|
+
|
20
|
+
Here is a summary of guides and their content:
|
21
|
+
|
22
|
+
<dl>
|
23
|
+
<dt>{file:docs/GettingStarted.textile Getting Started}</dt>
|
24
|
+
<dd>
|
25
|
+
Walks you through gem installation and 3 applications that demonstrate what AMQP has to offer. Explains how amqp gem should
|
26
|
+
be integrated into rich object-oriented Ruby programs.
|
27
|
+
</dd>
|
28
|
+
|
29
|
+
|
30
|
+
<dt>{file:docs/AMQP091ModelExplained.textile AMQP 0.9.1 Model Explained}</dt>
|
31
|
+
<dd>
|
32
|
+
Explains the AMQP 0.9.1 model, focusing on what purpose individual features have. Reading
|
33
|
+
other documentation guides will be easier when you are armed with this knowledge: a lot of the AMQP & RabbitMQ power
|
34
|
+
comes from the AMQP model.
|
35
|
+
</dd>
|
36
|
+
|
37
|
+
<dt>{file:docs/ConnectingToTheBroker.textile Connecting to the Broker}</dt>
|
38
|
+
<dd>
|
39
|
+
Connecting to AMQP broker. How to integrate with standalone applications as well as Ruby on Rails, Merb, Sinatra or Rack apps.
|
40
|
+
What difference application server (Unicorn, Passenger, Thin, etc) makes. How to handle authentication failures and network
|
41
|
+
connectivity issues. Closing AMQP connection gracefully.
|
42
|
+
</dd>
|
43
|
+
|
44
|
+
<dt>{file:docs/Queues.textile Working With Queues}</dt>
|
45
|
+
<dd>
|
46
|
+
What AMQP queues are. How to declare AMQP queues. When and how to use server-named and explicitly named queues. How to subscribe
|
47
|
+
for "push" message delivery. What message acknowledgements are. How to access AMQP message metadata. How to fetch ("pull") messages
|
48
|
+
from queues on demand. How to bind and unbind a queue to an exchange. How to delete a queue.
|
49
|
+
</dd>
|
50
|
+
|
51
|
+
<dt>{file:docs/Exchanges.textile Working With Exchanges}</dt>
|
52
|
+
<dd>
|
53
|
+
What AMQP exchanges are. Concept of binding. How to declare AMQP exchanges. How different exchange types route messages and common
|
54
|
+
use cases. How to publish messages, especially how to do it reliably. What AMQP transactions are. What Publisher Confirms are.
|
55
|
+
How to delete an exchange.
|
56
|
+
</dd>
|
57
|
+
|
58
|
+
<dt>{file:docs/PatternsAndUseCases.textile Patterns and Use Cases}</dt>
|
59
|
+
<dd>Typical use cases, patterns and routing topologies.</dd>
|
60
|
+
|
61
|
+
<dt>{file:docs/Durability.textile Durability and Message Persistence}</dt>
|
62
|
+
<dd>Exchange durability. Queue durability. Message persistence. Performance implications.</dd>
|
63
|
+
|
64
|
+
<dt>{file:docs/ErrorHandling.textile Error Handling and Recovery}</dt>
|
65
|
+
<dd>
|
66
|
+
Network failures. Connection-level exceptions. Channel-level exceptions. Why error handling is easy but
|
67
|
+
recovery is hard. How to survive typical problems. What other tools can help (e.g. HAProxy).
|
68
|
+
</dd>
|
69
|
+
|
70
|
+
<dt>{file:docs/RabbitMQVersions.textile RabbitMQ versions}</dt>
|
71
|
+
<dd>
|
72
|
+
RabbitMQ versions that amqp gem supports. Popular Linux distributions and RabbitMQ versions they ship. How to obtain up-to-date official
|
73
|
+
packages of RabbitMQ.
|
74
|
+
</dd>
|
75
|
+
|
76
|
+
<dt>{file:docs/ConnectionEncryptionWithTLS.textile Using TLS (SSL)}</dt>
|
77
|
+
<dd>All things related to TLS-encrypted connections.</dd>
|
78
|
+
</dl>
|
79
|
+
|
80
|
+
|
81
|
+
When more than one guide describes the same concept, we make sure to use cross-references, however, only one guide discusses the concept in detail.
|
82
|
+
|
83
|
+
|
84
|
+
h2. Full guide list
|
85
|
+
|
86
|
+
* {file:docs/GettingStarted.textile Getting Started}
|
87
|
+
* {file:docs/AMQP091ModelExplained.textile AMQP 0.9.1 Model Explained}
|
88
|
+
* {file:docs/ConnectingToTheBroker.textile Connecting to the Broker}
|
89
|
+
* {file:docs/Queues.textile Working With Queues}
|
90
|
+
* {file:docs/Exchanges.textile Working With Exchanges}
|
11
91
|
* {file:docs/Bindings.textile Bindings}
|
12
|
-
* {file:docs/
|
13
|
-
* {file:docs/Durability.textile Durability and
|
14
|
-
* {file:docs/ErrorHandling.textile Error
|
92
|
+
* {file:docs/PatternsAndUseCases.textile Patterns and Use Cases}
|
93
|
+
* {file:docs/Durability.textile Durability and Message Persistence}
|
94
|
+
* {file:docs/ErrorHandling.textile Error Handling and Recovery}
|
15
95
|
* {file:docs/08Migration.textile Upgrading from version 0.6.x/0.7.x to 0.8.x and above}
|
16
96
|
* {file:docs/Troubleshooting.textile Troubleshooting and debugging AMQP applications}
|
17
97
|
* {file:docs/Clustering.textile Clustering}
|
18
98
|
* {file:docs/RabbitMQVersions.textile RabbitMQ versions}
|
19
99
|
* {file:docs/ConnectionEncryptionWithTLS.textile Using TLS (SSL)}
|
20
|
-
* {file:docs/VendorSpecificExtensions.textile Vendor-specific
|
100
|
+
* {file:docs/VendorSpecificExtensions.textile Vendor-specific Extensions to AMQP 0.9.1 spec}
|
21
101
|
* {file:docs/RunningTests.textile Running amqp gem test suite}
|
22
102
|
|
23
103
|
|
24
104
|
h2. Tell us what you think!
|
25
105
|
|
26
|
-
Please take a moment
|
27
|
-
what was unclear
|
28
|
-
key to making documentation better.
|
106
|
+
Please take a moment to tell us what you think about this guide "on Twitter":http://twitter.com/rubyamqp or the "Ruby AMQP mailing list":http://groups.google.com/group/ruby-amqp.
|
107
|
+
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
|
108
|
+
key to making the documentation better.
|
29
109
|
|
30
|
-
If
|
110
|
+
If, for some reason, you cannot use the communication channels mentioned above, you can "contact the author of the guides directly":mailto:michael@novemberain.com?subject=amqp%20gem%20documentation
|
31
111
|
|
32
112
|
|
33
113
|
<div id="disqus_thread"></div>
|
data/docs/Durability.textile
CHANGED
@@ -98,7 +98,7 @@ See {file:docs/Clustering.textile Clustering guide} for in-depth discussion of t
|
|
98
98
|
|
99
99
|
h2. Tell us what you think!
|
100
100
|
|
101
|
-
Please take a moment and tell us what you think about this guide on "Ruby AMQP mailing list":http://groups.google.com/group/ruby-amqp:
|
101
|
+
Please take a moment and tell us what you think about this guide "on Twitter":http://twitter.com/rubyamqp or "Ruby AMQP mailing list":http://groups.google.com/group/ruby-amqp:
|
102
102
|
what was unclear? what wasn't covered? maybe you don't like guide style or grammar and spelling are incorrect? Readers feedback is
|
103
103
|
key to making documentation better.
|
104
104
|
|
data/docs/ErrorHandling.textile
CHANGED
@@ -9,17 +9,23 @@ multiple kinds of failures: protocol exceptions, network failures, broker failur
|
|
9
9
|
Correct error handling and recovery is not easy. This guide explains how amqp gem helps you in dealing with
|
10
10
|
issues like
|
11
11
|
|
12
|
-
*
|
12
|
+
* Initial broker connection failures
|
13
13
|
* Network connection interruption
|
14
|
-
* TLS (SSL) related issues
|
15
14
|
* AMQP connection-level exceptions
|
16
15
|
* AMQP channel-level exceptions
|
17
16
|
* Broker failure
|
17
|
+
* TLS (SSL) related issues
|
18
|
+
|
19
|
+
as well as
|
20
|
+
|
21
|
+
* How to recover after a network failure
|
22
|
+
* What is automatic recovery mode, when you should and should not use it
|
23
|
+
|
18
24
|
|
19
25
|
|
20
26
|
h2. Covered versions
|
21
27
|
|
22
|
-
This guide covers "Ruby amqp gem":http://github.com/ruby-amqp/amqp v0.8.0 and later.
|
28
|
+
This guide covers "Ruby amqp gem":http://github.com/ruby-amqp/amqp v0.8.0.RC14 and later.
|
23
29
|
|
24
30
|
|
25
31
|
h2. Code examples
|
@@ -28,7 +34,7 @@ There are several {https://github.com/ruby-amqp/amqp/tree/master/examples/error_
|
|
28
34
|
free to contribute new examples.
|
29
35
|
|
30
36
|
|
31
|
-
h3.
|
37
|
+
h3. Initial broker connection failures
|
32
38
|
|
33
39
|
When applications connect to the broker, they need to handle connection failures. Networks are not 100% reliable, even with modern system configuration tools
|
34
40
|
like Chef or Puppet misconfigurations happen and broker might be down, too. Error detection should happen as early as possible. There are two ways of detecting
|
@@ -36,24 +42,6 @@ TCP connection failure, the first one is to catch an exception:
|
|
36
42
|
|
37
43
|
<pre>
|
38
44
|
<code>
|
39
|
-
#!/usr/bin/env ruby
|
40
|
-
# encoding: utf-8
|
41
|
-
|
42
|
-
require "rubygems"
|
43
|
-
require "amqp"
|
44
|
-
|
45
|
-
|
46
|
-
puts "=> TCP connection failure handling with a rescue statement"
|
47
|
-
puts
|
48
|
-
|
49
|
-
connection_settings = {
|
50
|
-
:port => 9689,
|
51
|
-
:vhost => "/amq_client_testbed",
|
52
|
-
:user => "amq_client_gem",
|
53
|
-
:password => "amq_client_gem_password",
|
54
|
-
:timeout => 0.3
|
55
|
-
}
|
56
|
-
|
57
45
|
begin
|
58
46
|
AMQP.start(connection_settings) do |connection, open_ok|
|
59
47
|
raise "This should not be reachable"
|
@@ -64,6 +52,9 @@ end
|
|
64
52
|
</code>
|
65
53
|
</pre>
|
66
54
|
|
55
|
+
Full example:
|
56
|
+
<script src="https://gist.github.com/1016238.js"> </script>
|
57
|
+
|
67
58
|
{AMQP.connect} (and {AMQP.start}) will raise {AMQP::TCPConnectionFailed} if connection fails. Code that catches it can write to log
|
68
59
|
about the issue or use retry to execute begin block one more time. Because initial connection failures are due to misconfiguration or network outage, reconnection
|
69
60
|
to the same endpoint (hostname, port, vhost combination) will result in the same issue over and over. TBD: failover, connection to the cluster.
|
@@ -72,16 +63,7 @@ Alternative way of handling connection failure is with an errback (a callback fo
|
|
72
63
|
|
73
64
|
<pre>
|
74
65
|
<code>
|
75
|
-
|
76
|
-
# encoding: utf-8
|
77
|
-
|
78
|
-
require "rubygems"
|
79
|
-
require "amqp"
|
80
|
-
|
81
|
-
puts "=> TCP connection failure handling with a callback"
|
82
|
-
puts
|
83
|
-
|
84
|
-
handler = Proc.new { |settings| puts "Failed to connect, as expected"; EM.stop }
|
66
|
+
handler = Proc.new { |settings| puts "Failed to connect, as expected"; EventMachine.stop }
|
85
67
|
connection_settings = {
|
86
68
|
:port => 9689,
|
87
69
|
:vhost => "/amq_client_testbed",
|
@@ -90,14 +72,12 @@ connection_settings = {
|
|
90
72
|
:timeout => 0.3,
|
91
73
|
:on_tcp_connection_failure => handler
|
92
74
|
}
|
93
|
-
|
94
|
-
|
95
|
-
AMQP.start(connection_settings) do |connection, open_ok|
|
96
|
-
raise "This should not be reachable"
|
97
|
-
end
|
98
75
|
</code>
|
99
76
|
</pre>
|
100
77
|
|
78
|
+
Full example:
|
79
|
+
<script src="https://gist.github.com/1016235.js"> </script>
|
80
|
+
|
101
81
|
:on_tcp_connection_failure option accepts any object that responds to #call.
|
102
82
|
|
103
83
|
If you connect to the broker from a code in a class (as opposed to top-level scope in a script), Object#method can be used to pass object method as a handler
|
@@ -111,126 +91,510 @@ h3. Authentication failures
|
|
111
91
|
Another reason why connection may fail is authentication failure. Handling authentication failure is very similar to handling initial TCP
|
112
92
|
connection failure:
|
113
93
|
|
94
|
+
<script src="https://gist.github.com/1016233.js"> </script>
|
95
|
+
|
96
|
+
|
97
|
+
h4. Default handler
|
98
|
+
|
99
|
+
default handler raises {AMQP::PossibleAuthenticationFailureError}:
|
100
|
+
|
101
|
+
<script src="https://gist.github.com/1016234.js"> </script>
|
102
|
+
|
103
|
+
In case you wonder why callback name has "possible" in it: {http://bit.ly/mTr1YN AMQP 0.9.1 spec} requires broker implementations to
|
104
|
+
simply close TCP connection without sending any more data when an exception (such as authentication failure) occurs before AMQP connection
|
105
|
+
is open. In practice, however, when broker closes TCP connection between successful TCP connection and before AMQP connection is open,
|
106
|
+
it means that authentication has failed.
|
107
|
+
|
108
|
+
|
109
|
+
|
110
|
+
h2. Handling network connection interruptions
|
111
|
+
|
112
|
+
Network connectivity issues are sad fact of life in modern software systems. Event small products and projects these days consist of multiple
|
113
|
+
applications, often running on more than one machine. Ruby amqp gem detects TCP connection failures and lets you handle them by
|
114
|
+
defining a callback using {AMQP::Session#on_tcp_connection_loss}. That callback will be run when TCP connection fails, and will be passed
|
115
|
+
two parameters: connection object and settings of the last successful connection.
|
116
|
+
|
114
117
|
<pre>
|
115
118
|
<code>
|
116
|
-
|
117
|
-
#
|
119
|
+
connection.on_tcp_connection_loss do |connection, settings|
|
120
|
+
# reconnect in 10 seconds, without enforcement
|
121
|
+
connection.reconnect(false, 10)
|
122
|
+
end
|
123
|
+
</code>
|
124
|
+
</pre>
|
118
125
|
|
119
|
-
|
120
|
-
|
126
|
+
Sometimes it is necessary for other entities in an application to react to network failures. amqp gem 0.8.0 and later provides a number event
|
127
|
+
handlers to make this task easier for developers. This set of features is know as the "shutdown protocol" (the word "protocol" here means
|
128
|
+
"API interface" or "behavior", not network protocol).
|
121
129
|
|
122
|
-
|
123
|
-
|
130
|
+
{AMQP::Session}, {AMQP::Channel}, {AMQP::Exchange}, {AMQP::Queue} and {AMQP::Consumer all implement shutdown protocol and thus error
|
131
|
+
handling API is consistent for all classes, with {AMQP::Session} and #{AMQP::Channel} having a few methods other entities do not have.
|
124
132
|
|
125
|
-
|
126
|
-
connection_settings = {
|
127
|
-
:port => 5672,
|
128
|
-
:vhost => "/amq_client_testbed",
|
129
|
-
:user => "amq_client_gem",
|
130
|
-
:password => "amq_client_gem_password_that_is_incorrect #{Time.now.to_i}",
|
131
|
-
:timeout => 0.3,
|
132
|
-
:on_tcp_connection_failure => handler,
|
133
|
-
:on_possible_authentication_failure => Proc.new { |settings|
|
134
|
-
puts "Authentication failed, as expected, settings are: #{settings.inspect}"
|
133
|
+
The Shutdown protocol revolves around two events:
|
135
134
|
|
136
|
-
|
137
|
-
|
138
|
-
|
135
|
+
* Network connection fails
|
136
|
+
* Broker closes AMQP connection (or channel)
|
137
|
+
|
138
|
+
In this section, we concentrate only on the former. When network connection fails, the underlying networking library detects it and
|
139
|
+
runs a piece of code on {AMQP::Session} to handle it. That, in turn, propagates this event to channels, channels propagate it to
|
140
|
+
exchanges and queues, queues propagate it to their consumers (if any). Each of these entities in the object graph can react
|
141
|
+
to network interruption by executing application-defined callbacks.
|
142
|
+
|
143
|
+
h3. Shutdown Protocol methods on AMQP::Session
|
139
144
|
|
140
|
-
|
141
|
-
|
145
|
+
* {AMQP::Session#on_tcp_connection_loss}
|
146
|
+
* {AMQP::Session#on_connection_interruption}
|
147
|
+
|
148
|
+
The difference between these methods is that {AMQP::Session#on_tcp_connection_loss} is used to define a callback that will
|
149
|
+
be executed *once* when TCP connection fails. It is possible that reconnection attempts will not succeed immediately, so
|
150
|
+
there will be subsequent failures. To react to those, {AMQP::Session#on_connection_interruption} method is used.
|
151
|
+
|
152
|
+
First argument that both of these methods yield to the handler your application defines is the connection itself. This is
|
153
|
+
done to make sure you can register Ruby objects as handlers, and they do not have to keep any state around (for example,
|
154
|
+
connection instances):
|
155
|
+
|
156
|
+
<pre>
|
157
|
+
<code>
|
158
|
+
connection.on_connection_interruption do |conn|
|
159
|
+
puts "Connection detected connection interruption"
|
142
160
|
end
|
161
|
+
|
162
|
+
# or
|
163
|
+
|
164
|
+
class ConnectionInterruptionHandler
|
165
|
+
|
166
|
+
#
|
167
|
+
# API
|
168
|
+
#
|
169
|
+
|
170
|
+
def handle(connection)
|
171
|
+
# handling logic
|
172
|
+
end
|
173
|
+
|
174
|
+
end
|
175
|
+
|
176
|
+
handler = ConnectionInterruptionHandler.new
|
177
|
+
connection.on_connection_interruption(&handler.method(:handle))
|
143
178
|
</code>
|
144
179
|
</pre>
|
145
180
|
|
146
|
-
|
181
|
+
Note that {AMQP::Session#on_connection_interruption} callback is called *before* this event is propagated to channels, queues and so on.
|
182
|
+
|
183
|
+
Different applications handle connection failures differently. It is very common to use {AMQP::Session#reconnect} method
|
184
|
+
to schedule a reconnection to the same host, or use {AMQP::Session#reconnect_to} to connect to a different one.
|
185
|
+
|
186
|
+
For some applications it is OK to simply exit and wait to be restarted at a later point in time, for example, by a process
|
187
|
+
monitoring system like Nagios or Monit.
|
188
|
+
|
189
|
+
|
190
|
+
h3. Shutdown Protocol methods on AMQP::Channel
|
191
|
+
|
192
|
+
{AMQP::Channel} provides only one method: {AMQP::Channel#on_connection_interruption}, that registers a callback similar to
|
193
|
+
the one seen in the previous section:
|
147
194
|
|
148
195
|
<pre>
|
149
196
|
<code>
|
150
|
-
|
151
|
-
#
|
197
|
+
channel.on_connection_interruption do |ch|
|
198
|
+
puts "Channel #{ch.id} detected connection interruption"
|
199
|
+
end
|
200
|
+
</code>
|
201
|
+
</pre>
|
152
202
|
|
153
|
-
|
154
|
-
|
203
|
+
Note that {AMQP::Channel#on_connection_interruption} callback is called *after* this event is propagated to exchanges, queues and so on.
|
204
|
+
Right after that channel state is reset, except for error handling/recovery-related callbacks.
|
155
205
|
|
156
|
-
|
157
|
-
|
206
|
+
<span class="note">
|
207
|
+
Many applications do not need per-channel network failure handling.
|
208
|
+
</span>
|
158
209
|
|
159
|
-
handler = Proc.new { |settings| puts "Failed to connect, as expected"; EM.stop }
|
160
|
-
connection_settings = {
|
161
|
-
:port => 5672,
|
162
|
-
:vhost => "/amq_client_testbed",
|
163
|
-
:user => "amq_client_gem",
|
164
|
-
:password => "amq_client_gem_password_that_is_incorrect #{Time.now.to_i}",
|
165
|
-
:timeout => 0.3,
|
166
|
-
:on_tcp_connection_failure => handler
|
167
|
-
}
|
168
210
|
|
211
|
+
h3. Shutdown Protocol methods on AMQP::Exchange
|
169
212
|
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
213
|
+
{AMQP::Exchange} provides only one method: {AMQP::Exchange#on_connection_interruption}, that registers a callback similar to
|
214
|
+
the one seen in the previous section:
|
215
|
+
|
216
|
+
<pre>
|
217
|
+
<code>
|
218
|
+
exchange.on_connection_interruption do |ex|
|
219
|
+
puts "Exchange #{ex.name} detected connection interruption"
|
177
220
|
end
|
178
221
|
</code>
|
179
222
|
</pre>
|
180
223
|
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
it means that authentication has failed.
|
224
|
+
<span class="note">
|
225
|
+
Many applications do not need per-exchange network failure handling.
|
226
|
+
</span>
|
185
227
|
|
186
228
|
|
229
|
+
h3. Shutdown Protocol methods on AMQP::Queue
|
187
230
|
|
188
|
-
|
231
|
+
{AMQP::Queue} provides only one method: {AMQP::Queue#on_connection_interruption}, that registers a callback similar to
|
232
|
+
the one seen in the previous section:
|
189
233
|
|
190
|
-
|
234
|
+
<pre>
|
235
|
+
<code>
|
236
|
+
queue.on_connection_interruption do |q|
|
237
|
+
puts "Queue #{q.name} detected connection interruption"
|
238
|
+
end
|
239
|
+
</code>
|
240
|
+
</pre>
|
191
241
|
|
242
|
+
Note that {AMQP::Queue#on_connection_interruption} callback is called *after* this event is propagated to consumers.
|
192
243
|
|
244
|
+
<span class="note">
|
245
|
+
Many applications do not need per-queue network failure handling.
|
246
|
+
</span>
|
193
247
|
|
194
|
-
h2. TLS (SSL) related issues
|
195
248
|
|
196
|
-
|
249
|
+
h3. Shutdown Protocol methods on AMQP::Consumer
|
250
|
+
|
251
|
+
{AMQP::Consumer} provides only one method: {AMQP::Consumer#on_connection_interruption}, that registers a callback similar to
|
252
|
+
the one seen in the previous section:
|
253
|
+
|
254
|
+
<pre>
|
255
|
+
<code>
|
256
|
+
consumer.on_connection_interruption do |c|
|
257
|
+
puts "Consumer with consumer tag #{c.consumer_tag} detected connection interruption"
|
258
|
+
end
|
259
|
+
</code>
|
260
|
+
</pre>
|
261
|
+
|
262
|
+
<span class="note">
|
263
|
+
Many applications do not need per-consumer network failure handling.
|
264
|
+
</span>
|
265
|
+
|
266
|
+
|
267
|
+
|
268
|
+
h2. Recovering from network connection failures
|
269
|
+
|
270
|
+
Detecting network connections is nearly useless if AMQP-based application cannot recover from them. Recovery is the hard part
|
271
|
+
in "error handling and recovery". Fortunately, recovery process for many applications follows one simple scheme that amqp
|
272
|
+
gem can perform automatically for you.
|
273
|
+
|
274
|
+
h3. Manual recovery
|
275
|
+
|
276
|
+
Similarly to the Shutdown Protocol, amqp gem entities implement Recovery Protocol. Recovery Protocol consists of 3 methods
|
277
|
+
connections, channels, queues, consumers and exchanges implement:
|
278
|
+
|
279
|
+
* {AMQP::Session#before_recovery}
|
280
|
+
* {AMQP::Session#auto_recover}
|
281
|
+
* {AMQP::Session#after_recovery}
|
282
|
+
|
283
|
+
{AMQP::Session#before_recovery} lets application developers register a callback that will be executed *after TCP connection is
|
284
|
+
re-established but before AMQP connection is reopened*. {AMQP::Session#after_recovery} is similar except that the callback is run
|
285
|
+
*after AMQP connection is reopened*.
|
286
|
+
|
287
|
+
{AMQP::Channel}, {AMQP::Queue}, {AMQP::Consumer} and {AMQP::Exchange} methods behavior is identical.
|
288
|
+
|
289
|
+
|
290
|
+
h3. Automatic recovery
|
291
|
+
|
292
|
+
Many applications use the same recovery strategy, that consists of the following steps:
|
293
|
+
|
294
|
+
* Re-open channels
|
295
|
+
* For each channel, re-declare exchanges
|
296
|
+
* For each channel, re-declare queues
|
297
|
+
* For each queue, recover all bindings
|
298
|
+
* For each queue, recover all consumers
|
299
|
+
|
300
|
+
amqp gem provides a feature known as "automatic recovery" that is *opt-in* (not opt-out, not used by default) and lets application
|
301
|
+
developers get aforementioned recovery strategy by setting one additional attribute on {AMQP::Channel} instance:
|
302
|
+
|
303
|
+
<pre>
|
304
|
+
<code>
|
305
|
+
ch = AMQP::Channel.new(connection)
|
306
|
+
ch.auto_recovery = true
|
307
|
+
</code>
|
308
|
+
</pre>
|
309
|
+
|
310
|
+
|
311
|
+
A more verbose way to do the same thing:
|
312
|
+
<pre>
|
313
|
+
<code>
|
314
|
+
ch = AMQP::Channel.new(connection, AMQP::Channel.next_channel_id, :auto_recovery => true)
|
315
|
+
</code>
|
316
|
+
</pre>
|
317
|
+
|
318
|
+
Note that if you do not want to pass any options, 2nd argument can be left out as well,
|
319
|
+
then it will default to {AMQP::Channel.next_channel_id}.
|
320
|
+
|
321
|
+
|
322
|
+
To find out whether channel uses automatic recovery mode, use {AMQP::Channel#auto_recovering?}.
|
323
|
+
|
324
|
+
Auto recovery mode can be turned on and off any number of times during channel life cycle, although very small percentage of
|
325
|
+
applications really does this. Typically you decide what channels should be using automatic recovery at application design
|
326
|
+
stage.
|
327
|
+
|
328
|
+
Full example (run it, then shut down AMQP broker running on localhost, then bring it back up and use management tools such as `rabbitmqctl`
|
329
|
+
to see that queues & bindings & consumer have all recovered):
|
330
|
+
<script src="https://gist.github.com/1048076.js"> </script>
|
331
|
+
|
332
|
+
Server-named queues, when recovered automatically, will get *new server-generated names* to guarantee there are no name collisions.
|
333
|
+
|
334
|
+
<span note="class">
|
335
|
+
When in doubt, try using automatic recovery first. If it is not sufficient for you application, switch to manual
|
336
|
+
recovery using events and callbacks introduced in the "Manual recovery" section.
|
337
|
+
</span>
|
338
|
+
|
339
|
+
|
340
|
+
h2. Detecting broker failures
|
341
|
+
|
342
|
+
AMQP applications see broker failure as TCP connection loss. There is no reliable way to know whether there is a network split
|
343
|
+
or network peer is down.
|
344
|
+
|
197
345
|
|
198
346
|
|
199
347
|
|
200
348
|
h2. AMQP connection-level exceptions
|
201
349
|
|
350
|
+
h3. Handling connection-level exceptions
|
351
|
+
|
352
|
+
Connection-level exceptions are rare and may indicate a serious issue with client library or in-flight data corruption. They mandate
|
353
|
+
that connection cannot be used any more and must be closed. In any case, your application should be prepared to handle this kind of errors.
|
354
|
+
To define a handler, use {AMQP::Session#on_error} method that takes a callback and yields two arguments to it when connection-level exception happens:
|
355
|
+
|
356
|
+
<pre>
|
357
|
+
<code>
|
358
|
+
connection.on_error do |conn, connection_close|
|
359
|
+
puts "Handling a connection-level exception."
|
360
|
+
puts
|
361
|
+
puts "AMQP class id : #{connection_close.class_id}"
|
362
|
+
puts "AMQP method id: #{connection_close.method_id}"
|
363
|
+
puts "Status code : #{connection_close.reply_code}"
|
364
|
+
puts "Error message : #{connection_close.reply_text}"
|
365
|
+
end
|
366
|
+
</code>
|
367
|
+
</pre>
|
368
|
+
|
369
|
+
Status codes are similar to those of HTTP. For the full list of error codes and their meaning, consult {http://www.rabbitmq.com/amqp-0-9-1-reference.html#constants AMQP 0.9.1 constants reference}.
|
370
|
+
|
371
|
+
<span class="note">
|
372
|
+
Only one connection-level exception handler can be defined per connection instance (the one added last replaces previously added ones).
|
373
|
+
</span>
|
374
|
+
|
375
|
+
Full example:
|
376
|
+
<script src="https://gist.github.com/1015966.js"> </script>
|
377
|
+
|
378
|
+
|
379
|
+
|
380
|
+
h2. Handling graceful broker shutdown
|
381
|
+
|
382
|
+
When AMQP broker is shut down, it properly closes connection first. To do so, it uses *connection.close* AMQP method. AMQP clients then
|
383
|
+
need to check if the reply code is equal to 320 (CONNECTION_FORCED) to distinguish graceful shutdown. With RabbitMQ, when broker
|
384
|
+
is stopped using
|
385
|
+
|
386
|
+
<pre>rabbitmqctl stop</pre>
|
387
|
+
|
388
|
+
reply_text will be set to
|
389
|
+
|
390
|
+
<pre>CONNECTION_FORCED - broker forced connection closure with reason 'shutdown'</pre>
|
391
|
+
|
392
|
+
Each application choose how to handle graceful broker shutdowns individually, so *amqp gem's automatic reconnection does not cover graceful broker shutdowns*.
|
393
|
+
Applications that want to reconnect when broker is stopped can use {AMQP::Session#periodically_reconnect} like so:
|
394
|
+
|
395
|
+
<pre>
|
396
|
+
<code>
|
397
|
+
connection.on_error do |conn, connection_close|
|
398
|
+
puts "[connection.close] Reply code = #{connection_close.reply_code}, reply text = #{connection_close.reply_text}"
|
399
|
+
if connection_close.reply_code == 320
|
400
|
+
puts "[connection.close] Setting up a periodic reconnection timer..."
|
401
|
+
# every 30 seconds
|
402
|
+
conn.periodically_reconnect(30)
|
403
|
+
end
|
404
|
+
end
|
405
|
+
</code>
|
406
|
+
</pre>
|
407
|
+
|
408
|
+
Once AMQP connection is re-opened, channels in automatic recovery mode will recover just like they do after network outages.
|
409
|
+
|
410
|
+
|
411
|
+
h2. Integrating channel-level exceptions handling with object-oriented Ruby code
|
412
|
+
|
413
|
+
Error handling can be easily integrated into object-oriented Ruby code (in fact, this is highly encouraged).
|
414
|
+
A common technique is to combine {http://rubydoc.info/stdlib/core/1.8.7/Object:method Object#method} and {http://rubydoc.info/stdlib/core/1.8.7/Method:to_proc Method#to_proc}
|
415
|
+
and use object methods as error handlers:
|
416
|
+
|
417
|
+
<pre>
|
418
|
+
<code>
|
419
|
+
class ConnectionManager
|
420
|
+
|
421
|
+
#
|
422
|
+
# API
|
423
|
+
#
|
424
|
+
|
425
|
+
def connect(*args, &block)
|
426
|
+
@connection = AMQP.connect(*args, &block)
|
427
|
+
|
428
|
+
# combines Object#method and Method#to_proc to use object
|
429
|
+
# method as a callback
|
430
|
+
@connection.on_error(&method(:on_error))
|
431
|
+
end # connect(*args, &block)
|
432
|
+
|
433
|
+
|
434
|
+
def on_error(connection, connection_close)
|
435
|
+
puts "Handling a connection-level exception."
|
436
|
+
puts
|
437
|
+
puts "AMQP class id : #{connection_close.class_id}"
|
438
|
+
puts "AMQP method id: #{connection_close.method_id}"
|
439
|
+
puts "Status code : #{connection_close.reply_code}"
|
440
|
+
puts "Error message : #{connection_close.reply_text}"
|
441
|
+
end # on_error(connection, connection_close)
|
442
|
+
end
|
443
|
+
</code>
|
444
|
+
</pre>
|
445
|
+
|
446
|
+
Full example that uses objects:
|
447
|
+
<script src="https://gist.github.com/1016049.js"> </script>
|
448
|
+
|
202
449
|
TBD
|
203
450
|
|
204
451
|
|
205
452
|
|
453
|
+
|
206
454
|
h2. AMQP channel-level exceptions
|
207
455
|
|
208
|
-
|
456
|
+
h3. Hanling channel-level exceptions
|
209
457
|
|
458
|
+
Channel-level exceptions are more common than connection-level ones. They are handled in a similar manner, by defining a callback
|
459
|
+
with {AMQP::Channel#on_error} method that takes a callback and yields two arguments to it when channel-level exception happens:
|
210
460
|
|
461
|
+
<pre>
|
462
|
+
<code>
|
463
|
+
channel.on_error do |ch, channel_close|
|
464
|
+
puts "Handling a channel-level exception."
|
465
|
+
puts
|
466
|
+
puts "AMQP class id : #{channel_close.class_id}"
|
467
|
+
puts "AMQP method id: #{channel_close.method_id}"
|
468
|
+
puts "Status code : #{channel_close.reply_code}"
|
469
|
+
puts "Error message : #{channel_close.reply_text}"
|
470
|
+
end
|
471
|
+
</code>
|
472
|
+
</pre>
|
211
473
|
|
212
|
-
|
474
|
+
Status codes are similar to those of HTTP. For the full list of error codes and their meaning, consult {http://www.rabbitmq.com/amqp-0-9-1-reference.html#constants AMQP 0.9.1 constants reference}.
|
213
475
|
|
214
|
-
|
476
|
+
<span class="note">
|
477
|
+
Only one channel-level exception handler can be defined per channel instance (the one added last replaces previously added ones).
|
478
|
+
</span>
|
479
|
+
|
480
|
+
Full example:
|
481
|
+
<script src="https://gist.github.com/1016042.js"> </script>
|
215
482
|
|
216
483
|
|
484
|
+
h3. Integrating channel-level exceptions handling with object-oriented Ruby code
|
217
485
|
|
486
|
+
Error handling can be easily integrated into object-oriented Ruby code (in fact, this is highly encouraged).
|
487
|
+
A common technique is to combine {http://rubydoc.info/stdlib/core/1.8.7/Object:method Object#method} and {http://rubydoc.info/stdlib/core/1.8.7/Method:to_proc Method#to_proc}
|
488
|
+
and use object methods as error handlers. For example of this, see section on connection-level exceptions above.
|
218
489
|
|
219
|
-
|
490
|
+
|
491
|
+
h3. Common channel-level exceptions and what they mean
|
492
|
+
|
493
|
+
A few channel-level exceptions are common and deserve more attention.
|
494
|
+
|
495
|
+
h4. 406 Precondition Failed
|
496
|
+
|
497
|
+
<dl>
|
498
|
+
<dt>Description</dt>
|
499
|
+
<dd>The client requested a method that was not allowed because some precondition failed.</dd>
|
500
|
+
|
501
|
+
<dt>What might cause it</dt>
|
502
|
+
<dd>
|
503
|
+
<ul>
|
504
|
+
<li>
|
505
|
+
AMQP entity (a queue or exchange) was re-declared with attributes different from original declaration. Maybe two applications or pieces of code
|
506
|
+
declare the same entity with different attributes. Note that different AMQP client libraries historically use slightly different defaults for
|
507
|
+
entities and this may cause attribute mismatches.
|
508
|
+
</li>
|
509
|
+
<li>{AMQP::Channel#tx_commit} or {AMQP::Channel#tx_rollback} might be run on a channel that wasn't previously made transactional with {AMQP::Channel#tx_select}</li>
|
510
|
+
</ul>
|
511
|
+
</dd>
|
512
|
+
|
513
|
+
<dt>Example RabbitMQ error message</dt>
|
514
|
+
<dd>
|
515
|
+
<ul>
|
516
|
+
<li>PRECONDITION_FAILED - parameters for queue 'amqpgem.examples.channel_exception' in vhost '/' not equivalent</li>
|
517
|
+
<li>PRECONDITION_FAILED - channel is not transactional</li>
|
518
|
+
</ul>
|
519
|
+
</dd>
|
520
|
+
</dl>
|
521
|
+
|
522
|
+
h4. 405 Resource Locked
|
523
|
+
|
524
|
+
<dl>
|
525
|
+
<dt>Description</dt>
|
526
|
+
<dd>The client attempted to work with a server entity to which it has no access because another client is working with it.</dd>
|
527
|
+
|
528
|
+
<dt>What might cause it</dt>
|
529
|
+
<dd>
|
530
|
+
<ul>
|
531
|
+
<li>Multiple applications (or different pieces of code/threads/processes/routines within a single application) might try to declare queues with the same name as exclusive.</li>
|
532
|
+
<li>Multiple consumer across multiple or single app might be registered as exclusive for the same queue.</li>
|
533
|
+
</ul>
|
534
|
+
</dd>
|
535
|
+
|
536
|
+
<dt>Example RabbitMQ error message</dt>
|
537
|
+
<dd>RESOURCE_LOCKED - cannot obtain exclusive access to locked queue 'amqpgem.examples.queue' in vhost '/'</dd>
|
538
|
+
</dl>
|
539
|
+
|
540
|
+
|
541
|
+
h4. 403 Access Refused
|
542
|
+
|
543
|
+
<dl>
|
544
|
+
<dt>Description</dt>
|
545
|
+
<dd>The client attempted to work with a server entity to which it has no access due to security settings. </dd>
|
546
|
+
|
547
|
+
<dt>What might cause it</dt>
|
548
|
+
<dd>Application tries to access a queue or exchange it has no permissions for (or right kind of permissions, for example, write permissions)</dd>
|
549
|
+
|
550
|
+
<dt>Example RabbitMQ error message</dt>
|
551
|
+
<dd>ACCESS_REFUSED - access to queue 'amqpgem.examples.channel_exception' in vhost 'amqp_gem_testbed' refused for user 'amqp_gem_reader'</dd>
|
552
|
+
</dl>
|
553
|
+
|
554
|
+
|
555
|
+
|
556
|
+
|
557
|
+
h2. TLS (SSL) related issues
|
220
558
|
|
221
559
|
TBD
|
222
560
|
|
223
561
|
|
224
562
|
|
563
|
+
|
564
|
+
|
225
565
|
h2. Conclusion
|
226
566
|
|
567
|
+
Distributed applications introduce a whole new class of failres developers need to be aware of. Many of them come from
|
568
|
+
unreliability of the network. The famous "Fallacies of Distributed Computing":http://en.wikipedia.org/wiki/Fallacies_of_Distributed_Computing list
|
569
|
+
common assumptions software engineers must not make:
|
570
|
+
|
571
|
+
* The network is reliable.
|
572
|
+
* Latency is zero.
|
573
|
+
* Bandwidth is infinite.
|
574
|
+
* The network is secure.
|
575
|
+
* Topology doesn't change.
|
576
|
+
* There is one administrator.
|
577
|
+
* Transport cost is zero.
|
578
|
+
* The network is homogeneous.
|
579
|
+
|
580
|
+
Unfortunately, applications that use Ruby and AMQP are not immune to these problems and developers need to always keep that
|
581
|
+
in mind. This list is just as relevant in 2011 as it was in the 90s.
|
582
|
+
|
583
|
+
Ruby amqp gem 0.8.x and later lets applications to define handlers that handle connection-level exceptions, channel-level
|
584
|
+
exceptions and TCP connection failures. Handling AMQP exceptions and network connection failures is relatively easy.
|
585
|
+
Re-declaring AMQP instances application works with is where the most of complexity comes from. By using Ruby objects as
|
586
|
+
error handlers, declaration of AMQP entities can be done in one place, making it much easier to understand and maintain.
|
587
|
+
|
588
|
+
amqp gem error handling and interruption is not a copy of RabbitMQ Java client's "Shutdown Protocol":http://www.rabbitmq.com/api-guide.html#shutdown,
|
589
|
+
but they turn out to be similar with respect to network failures and connection-level exceptions.
|
590
|
+
|
227
591
|
TBD
|
228
592
|
|
229
593
|
|
230
594
|
|
231
595
|
h2. Tell us what you think!
|
232
596
|
|
233
|
-
Please take a moment and tell us what you think about this guide on "Ruby AMQP mailing list":http://groups.google.com/group/ruby-amqp:
|
597
|
+
Please take a moment and tell us what you think about this guide "on Twitter":http://twitter.com/rubyamqp or "Ruby AMQP mailing list":http://groups.google.com/group/ruby-amqp:
|
234
598
|
what was unclear? what wasn't covered? maybe you don't like guide style or grammar and spelling are incorrect? Readers feedback is
|
235
599
|
key to making documentation better.
|
236
600
|
|