amqp 0.8.0.rc3 → 0.8.0.rc4

Sign up to get free protection for your applications and to get access to all the features.
data/CHANGELOG CHANGED
@@ -1,6 +1,7 @@
1
1
  = Version 0.8.0
2
2
 
3
3
  * [FEATURE] AMQP 0.9.1 support, including tx.* operations class.
4
+ * [API] Default authentication handler now raises AMQP::PossibleAuthenticationFailureError
4
5
  * [API] AMQP::Channel#initialize now takes 3rd (optional) options hash.
5
6
  * [API] Broker connection class is now AMQP::Session.
6
7
  * [API] AMQP::Error instance now may carry cause, an exception that caused exception in question to be raised.
@@ -29,7 +30,6 @@
29
30
  * [API] AMQP::Channel#recover now takes (an optional) callback that is called when basic.recover-ok is received.
30
31
  * [API] AMQP::Frame is gone.
31
32
  * [API] AMQP::Buffer is gone. Serialization & framing are now handled primarily by amq-protocol.
32
- * [FEATURE] AMQP gem is now AMQP 0.9.1 compatible: it runs atop of amq-client gem
33
33
  * [API] AMQP::Queue#publish is deprecated.
34
34
  * [API] Name argument for AMQP::Queue.new and Channel#queue is optional.
35
35
 
data/amqp.gemspec CHANGED
@@ -25,7 +25,7 @@ Gem::Specification.new do |s|
25
25
 
26
26
  # Dependencies
27
27
  s.add_dependency "eventmachine"
28
- s.add_dependency "amq-client", ">= 0.7.0.alpha13"
28
+ s.add_dependency "amq-client", ">= 0.7.0.alpha16"
29
29
 
30
30
  begin
31
31
  require "changelog"
@@ -154,7 +154,7 @@ end
154
154
  </code>
155
155
  </pre>
156
156
 
157
- {AMQP.connect} (and, subsequentily, {AMQP.start}) will raise {AMQP::TCPConnectionFailed} if connection fails. Code that catches it can write to log
157
+ {AMQP.connect} (and {AMQP.start}) will raise {AMQP::TCPConnectionFailed} if connection fails. Code that catches it can write to log
158
158
  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
159
159
  to the same endpoint (hostname, port, vhost combination) will result in the same issue over and over. TBD: failover, connection to the cluster.
160
160
 
@@ -209,7 +209,7 @@ connection failure:
209
209
  require "rubygems"
210
210
  require "amqp"
211
211
 
212
- puts "=> TCP connection failure handling with a callback"
212
+ puts "=> Authentication failure handling with a callback"
213
213
  puts
214
214
 
215
215
  handler = Proc.new { |settings| puts "Failed to connect, as expected"; EM.stop }
@@ -233,6 +233,41 @@ end
233
233
  </code>
234
234
  </pre>
235
235
 
236
+ default handler raises {AMQP::PossibleAuthenticationFailureError}:
237
+
238
+ <pre>
239
+ <code>
240
+ #!/usr/bin/env ruby
241
+ # encoding: utf-8
242
+
243
+ require "rubygems"
244
+ require "amqp"
245
+
246
+ puts "=> Authentication failure handling with a rescue block"
247
+ puts
248
+
249
+ handler = Proc.new { |settings| puts "Failed to connect, as expected"; EM.stop }
250
+ connection_settings = {
251
+ :port => 5672,
252
+ :vhost => "/amq_client_testbed",
253
+ :user => "amq_client_gem",
254
+ :password => "amq_client_gem_password_that_is_incorrect #{Time.now.to_i}",
255
+ :timeout => 0.3,
256
+ :on_tcp_connection_failure => handler
257
+ }
258
+
259
+
260
+ begin
261
+ AMQP.start(connection_settings) do |connection, open_ok|
262
+ raise "This should not be reachable"
263
+ end
264
+ rescue AMQP::PossibleAuthenticationFailureError => afe
265
+ puts "Authentication failed, as expected, caught #{afe.inspect}"
266
+ EventMachine.stop if EventMachine.reactor_running?
267
+ end
268
+ </code>
269
+ </pre>
270
+
236
271
  In case you wonder why callback name has "possible" in it: {http://bit.ly/mTr1YN AMQP 0.9.1 spec} requires broker implementations to
237
272
  simply close TCP connection without sending any more data when an exception (such as authentication failure) occurs before AMQP connection
238
273
  is open. In practice, however, when broker closes TCP connection between successful TCP connection and before AMQP connection is open,
@@ -242,30 +277,80 @@ it means that authentication has failed.
242
277
 
243
278
  h2. In Web applications (Ruby on Rails, Sinatra, Merb, Rack)
244
279
 
245
- h3. With Unicorn
280
+ Web applications are different from standalone applications in that main thread is occupied by Web/application server like Unicorn
281
+ or Thin, so you need to start EventMachine reactor before you attempt to use {AMQP.connect}.
282
+ In a Ruby on Rails app, probably the best place for this is in initializer (like config/initializers/amqp.rb). For Merb apps, it is config/init.rb.
283
+ For Sinatra and pure Rack applications, place it next to other configuration code.
284
+
285
+ Next we are going to discuss issues specific to particular Web servers.
246
286
 
247
- TBD
287
+ h3. Using amqp gem with Unicorn
248
288
 
289
+ h4. Use separate thread for EventMachine reactor
249
290
 
250
- h3. With Thin
291
+ Since Unicorn is not EventMachine-based, you need to start EventMachine reactor in a separate thread like this:
292
+
293
+ <pre>
294
+ <code>
295
+ Thread.new { EventMachine.run }
296
+ # give EventMachine reactor a moment to start
297
+ sleep(0.5)
251
298
 
252
- TBD
299
+ # now is a good moment to use AMQP.connect
300
+ </code>
301
+ </pre>
253
302
 
303
+ Otherwise EventMachine will block current thread.
254
304
 
255
- h3. With Goliath
305
+ h4. Starting EventMachine reactor after Unicorn forks worker processes
256
306
 
257
- TBD
307
+ Because *Unicorn is a pre-forking server, you need to run the same piece of code in
308
+ after_fork hook in Unicorn configuration file for your app*, otherwise, worker processes won't have EventMachine reactor running:
258
309
 
310
+ <pre>
311
+ <code>
312
+ # example snippet of Unicorn configuration file ( config/unicorn/development.rb or similar)
313
+ after_fork do |server, worker|
314
+ Thread.new { EventMachine.run }
315
+ # give EventMachine reactor a moment to start
316
+ sleep(0.5)
259
317
 
260
- h3. With Passenger
318
+ # now is a good moment to use AMQP.connect
319
+ end
320
+ </code>
321
+ </pre>
322
+
323
+
324
+ h3. Using amqp gem with Passenger
325
+
326
+ TBD: if you are a Passenger user, please help us write this section!
327
+
328
+
329
+
330
+ h3. Using amqp gem with Thin and Goliath
331
+
332
+ h4. Thin and Goliath start EventMachine reactor for you, but there is a little nuance
333
+
334
+ If you use "Thin":http://code.macournoyer.com/thin/ or "Goliath":https://github.com/postrank-labs/goliath/, you are all set: those two servers use EventMachine under the hood.
335
+ There is no need to start EventMachine reactor. However, depending on app server, it's version, version of the framework and Rack middleware being used,
336
+ EventMachine reactor start may be slightly delayed. To not depend on this factor, use EventMachine.next_tick to delay connection until after reactor is actually running:
337
+
338
+ <pre>
339
+ <code>
340
+ EventMachine.next_tick { AMQP.connect(...) }
341
+ </code>
342
+ </pre>
261
343
 
262
- TBD
344
+ So in case EventMachine reactor isn't running yet on server/application boot, connection won't fail but instead wait for reactor to start.
345
+ Thin and Goliath are not pre-forking servers so there is no need to re-establish connection the way you do it with Unicorn and Passenger.
263
346
 
264
347
 
265
348
 
266
349
  h2. What to read next
267
350
 
268
- TBD
351
+ * {file:docs/Queues.textile Queues}
352
+ * {file:docs/ErrorHandling.textile Error handling}
353
+ * {file:docs/ConnectionEncryptionWithTLS.textile Using TLS (SSL)} (if you want to use SSL encrypted connection to the broker)
269
354
 
270
355
 
271
356
  h2. Tell us what you think!
@@ -12,7 +12,7 @@ h2. Guide list
12
12
  * {file:docs/ErrorHandling.textile Error handling}
13
13
  * {file:docs/08Migration.textile Upgrading from version 0.6.x/0.7.x to 0.8.x and above}
14
14
  * {file:docs/RabbitMQVersions.textile RabbitMQ versions}
15
- * {file:docs/TLS.textile Using TLS (SSL)}
15
+ * {file:docs/ConnectionEncryptionWithTLS.textile Using TLS (SSL)}
16
16
  * {file:docs/VendorSpecificExtensions.textile Vendor-specific extensions to AMQP 0.9.1 spec}
17
17
 
18
18
 
@@ -1,4 +1,4 @@
1
- h1. Handling AMQP exceptions and connection failures
1
+ h1. Error handling and recovery
2
2
 
3
3
  h2. About this guide
4
4
 
@@ -26,9 +26,160 @@ There are several {https://github.com/ruby-amqp/amqp/tree/master/examples/error_
26
26
  free to contribute new examples.
27
27
 
28
28
 
29
- h2. Broker connection failures
29
+ h3. Broker connection failures
30
30
 
31
- TBD
31
+ When applications connect to the broker, they need to handle connection failures. Networks are not 100% reliable, even with modern system configuration tools
32
+ 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
33
+ TCP connection failure, the first one is to catch an exception:
34
+
35
+ <pre>
36
+ <code>
37
+ #!/usr/bin/env ruby
38
+ # encoding: utf-8
39
+
40
+ require "rubygems"
41
+ require "amqp"
42
+
43
+
44
+ puts "=> TCP connection failure handling with a rescue statement"
45
+ puts
46
+
47
+ connection_settings = {
48
+ :port => 9689,
49
+ :vhost => "/amq_client_testbed",
50
+ :user => "amq_client_gem",
51
+ :password => "amq_client_gem_password",
52
+ :timeout => 0.3
53
+ }
54
+
55
+ begin
56
+ AMQP.start(connection_settings) do |connection, open_ok|
57
+ raise "This should not be reachable"
58
+ end
59
+ rescue AMQP::TCPConnectionFailed => e
60
+ puts "Caught AMQP::TCPConnectionFailed => TCP connection failed, as expected."
61
+ end
62
+ </code>
63
+ </pre>
64
+
65
+ {AMQP.connect} (and {AMQP.start}) will raise {AMQP::TCPConnectionFailed} if connection fails. Code that catches it can write to log
66
+ 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
67
+ to the same endpoint (hostname, port, vhost combination) will result in the same issue over and over. TBD: failover, connection to the cluster.
68
+
69
+ Alternative way of handling connection failure is with an errback (a callback for specific kind of error):
70
+
71
+ <pre>
72
+ <code>
73
+ #!/usr/bin/env ruby
74
+ # encoding: utf-8
75
+
76
+ require "rubygems"
77
+ require "amqp"
78
+
79
+ puts "=> TCP connection failure handling with a callback"
80
+ puts
81
+
82
+ handler = Proc.new { |settings| puts "Failed to connect, as expected"; EM.stop }
83
+ connection_settings = {
84
+ :port => 9689,
85
+ :vhost => "/amq_client_testbed",
86
+ :user => "amq_client_gem",
87
+ :password => "amq_client_gem_password",
88
+ :timeout => 0.3,
89
+ :on_tcp_connection_failure => handler
90
+ }
91
+
92
+
93
+ AMQP.start(connection_settings) do |connection, open_ok|
94
+ raise "This should not be reachable"
95
+ end
96
+ </code>
97
+ </pre>
98
+
99
+ :on_tcp_connection_failure option accepts any object that responds to #call.
100
+
101
+ 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
102
+ instead of a Proc.
103
+
104
+ TBD: provide an example
105
+
106
+
107
+ h3. Authentication failures
108
+
109
+ Another reason why connection may fail is authentication failure. Handling authentication failure is very similar to handling initial TCP
110
+ connection failure:
111
+
112
+ <pre>
113
+ <code>
114
+ #!/usr/bin/env ruby
115
+ # encoding: utf-8
116
+
117
+ require "rubygems"
118
+ require "amqp"
119
+
120
+ puts "=> Authentication failure handling with a callback"
121
+ puts
122
+
123
+ handler = Proc.new { |settings| puts "Failed to connect, as expected"; EM.stop }
124
+ connection_settings = {
125
+ :port => 5672,
126
+ :vhost => "/amq_client_testbed",
127
+ :user => "amq_client_gem",
128
+ :password => "amq_client_gem_password_that_is_incorrect #{Time.now.to_i}",
129
+ :timeout => 0.3,
130
+ :on_tcp_connection_failure => handler,
131
+ :on_possible_authentication_failure => Proc.new { |settings|
132
+ puts "Authentication failed, as expected, settings are: #{settings.inspect}"
133
+
134
+ EM.stop
135
+ }
136
+ }
137
+
138
+ AMQP.start(connection_settings) do |connection, open_ok|
139
+ raise "This should not be reachable"
140
+ end
141
+ </code>
142
+ </pre>
143
+
144
+ default handler raises {AMQP::PossibleAuthenticationFailureError}:
145
+
146
+ <pre>
147
+ <code>
148
+ #!/usr/bin/env ruby
149
+ # encoding: utf-8
150
+
151
+ require "rubygems"
152
+ require "amqp"
153
+
154
+ puts "=> Authentication failure handling with a rescue block"
155
+ puts
156
+
157
+ handler = Proc.new { |settings| puts "Failed to connect, as expected"; EM.stop }
158
+ connection_settings = {
159
+ :port => 5672,
160
+ :vhost => "/amq_client_testbed",
161
+ :user => "amq_client_gem",
162
+ :password => "amq_client_gem_password_that_is_incorrect #{Time.now.to_i}",
163
+ :timeout => 0.3,
164
+ :on_tcp_connection_failure => handler
165
+ }
166
+
167
+
168
+ begin
169
+ AMQP.start(connection_settings) do |connection, open_ok|
170
+ raise "This should not be reachable"
171
+ end
172
+ rescue AMQP::PossibleAuthenticationFailureError => afe
173
+ puts "Authentication failed, as expected, caught #{afe.inspect}"
174
+ EventMachine.stop if EventMachine.reactor_running?
175
+ end
176
+ </code>
177
+ </pre>
178
+
179
+ In case you wonder why callback name has "possible" in it: {http://bit.ly/mTr1YN AMQP 0.9.1 spec} requires broker implementations to
180
+ simply close TCP connection without sending any more data when an exception (such as authentication failure) occurs before AMQP connection
181
+ is open. In practice, however, when broker closes TCP connection between successful TCP connection and before AMQP connection is open,
182
+ it means that authentication has failed.
32
183
 
33
184
 
34
185
 
@@ -11,12 +11,77 @@ h2. Covered versions
11
11
  This guide covers amqp gem v0.8.0 and later.
12
12
 
13
13
 
14
+ h2. Exchanges in AMQP 0.9.1, briefly
14
15
 
15
- h2. TBD
16
+ TBD
17
+
18
+
19
+ h2. Exchange types
20
+
21
+ TBD
22
+
23
+
24
+ h2. Fanout exchanges
16
25
 
17
26
  TBD
18
27
 
19
28
 
29
+ h2. Direct exchanges
30
+
31
+ TBD
32
+
33
+
34
+ h2. Topic exchanges
35
+
36
+ TBD
37
+
38
+
39
+
40
+ h2. Publishing messages
41
+
42
+ TBD
43
+
44
+
45
+
46
+ h2. Binding queues to exchanges
47
+
48
+ TBD
49
+
50
+
51
+
52
+ h2. Unbinding queues from exchanges
53
+
54
+ TBD
55
+
56
+
57
+ h2. Deleting exchanges
58
+
59
+ TBD
60
+
61
+
62
+
63
+ h2. Exchange durability vs Message durability
64
+
65
+ TBD
66
+
67
+
68
+
69
+ h2. Error handling and recovery
70
+
71
+ TBD
72
+
73
+
74
+
75
+ h2. Vendor-specific extensions related to exchanges
76
+
77
+ TBD
78
+
79
+
80
+
81
+ h2. What to read next
82
+
83
+ TBD
84
+
20
85
 
21
86
  h2. Tell us what you think!
22
87
 
data/docs/Queues.textile CHANGED
@@ -1,9 +1,10 @@
1
- h1. TBD
1
+ h1. AMQP queues in detail
2
2
 
3
3
 
4
4
  h2. About this guide
5
5
 
6
- TBD
6
+ This guide covers everything related to queues in AMQP 0.9.1, common usage scenarios and how to accomplish typical operations using
7
+ amqp gem.
7
8
 
8
9
 
9
10
  h2. Covered versions
@@ -12,12 +13,535 @@ This guide covers amqp gem v0.8.0 and later.
12
13
 
13
14
 
14
15
 
15
- h2. TBD
16
+ h2. Queues in AMQP 0.9.1, briefly
17
+
18
+ h3. What are AMQP queues?
19
+
20
+ Queues store and forward messages to consumers. They are similar to mailboxes in SMTP.
21
+ Messages flow from producing applications to {file:docs/Exchanges.textile exchanges} that route them
22
+ to queues and finally queues deliver them to consumer applications (or consumer applications fetch messages as needed).
23
+
24
+ Note that unlike some other messaging protocols/systems, messages are not delivered directly
25
+ to queues. They are delivered to exchanges that route messages to queues using rules
26
+ knows as *bindings*.
27
+
28
+ AMQP is a programmable protocol, so queues and bindings alike are declared by applications.
29
+
30
+
31
+ h3. Concept of bindings
32
+
33
+ Binding is an association between a queue and an exchange. Queues must be bound to at least one exchange in order to receive messages from publishers.
34
+ Learn more about bindings in {file:docs/Bindings.textile Bindings guide}.
35
+
36
+
37
+ h3. Attributes
38
+
39
+ Queues have several attributes associated with them:
40
+
41
+ * Name
42
+ * Exclusivity
43
+ * Whether queue is auto-deleted when no longer used
44
+ * Other metadata (aka X-arguments)
45
+
46
+ These attributes define how queues can be used, what their lifecycle is like and other aspects of queue
47
+ behavior.
48
+
49
+ amqp gem represents queues as instances of {AMQP::Queue}.
50
+
51
+ h2. Queue names. Server-named queues. Predefined queues.
52
+
53
+ Every queue has a name that identifies it. Queue names often contain several segments separated by a dot (.), similarly to how URI
54
+ path segments are separated by a slash (/), although it may be almost any string, with some limitations (see below).
55
+ Applications may pick queue names or ask broker to generate a name for them. To do so, pass *empty string* as queue name argument.
56
+
57
+ Here is an example:
58
+
59
+ <pre>
60
+ <code>
61
+ # Declaring a server-named queue using AMQP::Queue constructor
62
+ AMQP.start("amqp://guest:guest@dev.rabbitmq.com:5672/") do |connection, open_ok|
63
+ AMQP::Channel.new do |channel, open_ok|
64
+ AMQP::Queue.new(channel, "", :auto_delete => true) do |queue, declare_ok|
65
+ puts "#{queue.name} is ready to go. AMQP method: #{declare_ok.inspect}"
66
+
67
+ connection.close {
68
+ EventMachine.stop { exit }
69
+ }
70
+ end
71
+ end
72
+ end
73
+ </code>
74
+ </pre>
75
+
76
+ If you want to declare a queue with a particular name, for example, "images.resize", pass it to Queue class constructor:
77
+
78
+ <pre>
79
+ <code>
80
+ # Declaring a server-named queue using AMQP::Queue constructor
81
+ AMQP.start("amqp://guest:guest@dev.rabbitmq.com:5672/") do |connection, open_ok|
82
+ AMQP::Channel.new do |channel, open_ok|
83
+ AMQP::Queue.new(channel, "images.resize", :auto_delete => true) do |queue, declare_ok|
84
+ puts "#{queue.name} is ready to go."
85
+
86
+ connection.close {
87
+ EventMachine.stop { exit }
88
+ }
89
+ end
90
+ end
91
+ end
92
+ </code>
93
+ </pre>
94
+
95
+ Queue names starting with 'amq.' are reserved for internal use by the broker. Attempts to declare queue with a name that violates this
96
+ rule will result in AMQP::IncompatibleOptionsError to be thrown (when queue is re-declared on the same channel object) or channel-level exception
97
+ (when originally queue was declared on one channel and re-declaration with different attributes happens on another channel).
98
+ Learn more in Error handling and recovery section below.
99
+
100
+
101
+
102
+ h2. Common usage scenarios
103
+
104
+ h2. Queue life-cycles. When use of server-named queues is optimal and when it isn't.
105
+
106
+ To quote AMQP 0.9.1 spec, there are two common message queue life-cycles:
107
+
108
+ * Durable message queues that are shared by many consumers and have an independent existence: i.e. they
109
+ will continue to exist and collect messages whether or not there are consumers to receive them.
110
+ * Temporary message queues that are private to one consumer and are tied to that consumer. When the
111
+ consumer disconnects, the message queue is deleted.
112
+
113
+ There are some variations on these, such as shared message queues that are deleted when the last of
114
+ many consumers disconnects.
115
+
116
+ One example of durable message queues is well-known services like event collectors (event loggers).
117
+ They are usually up whether there are services to log anything or not. Other applications know what
118
+ queues they use and can rely on those queues being around all the time, survive broker restarts and
119
+ in general be available should an application in the network need to use them. In this case,
120
+ explicitly named durable queues are optimal and coupling it creates between applications is not
121
+ an issue. Another scenario of a well-known long-lived service is distributed metadata/directory/locking server
122
+ like Apache Zookeeper, Google's Chubby or DNS. Services like this benefit from using well-known, not generated
123
+ queue names, and so do other applications that use them.
124
+
125
+ Different scenario is in "a cloud settings" when some kind of workers/instances may come online and
126
+ go down basically any time and other applications cannot rely on them being available. Using well-known
127
+ queue names in this case is possible but server-generated, short-lived queues that are bound to
128
+ topic or fanout exchanges to receive relevant messages is a better idea.
129
+
130
+ Imagine a service that processes an endless stream of events (Twitter is one example). When traffic goes
131
+ up, development operations may spin up additional applications instances in the cloud to handle the load.
132
+ Those new instances want to subscribe to receive messages to process but the rest of the system doesn't
133
+ know anything about them, rely on them being online or try to address them directly: they process events
134
+ from a shared stream and are not different from their peers. In a case like this, there is no reason for
135
+ message consumers to not use queue names generated by the broker.
136
+
137
+ In general, use of explicitly named or server-named queues depends on messaging pattern your application needs.
138
+ {http://www.eaipatterns.com/ Enterprise Integration Patters} discusses many messaging patterns in depth.
139
+ RabbitMQ FAQ also has a section on {http://www.rabbitmq.com/faq.html#scenarios use cases}.
140
+
141
+
142
+
143
+ h2. Declaring a durable shared queue
144
+
145
+ To declare a durable shared queue, you pass queue name that is a non-blank string and use :durable option:
146
+
147
+ <pre>
148
+ <code>
149
+ #!/usr/bin/env ruby
150
+ # encoding: utf-8
151
+
152
+ require "rubygems"
153
+ require "amqp"
154
+
155
+ # Declaring a client-named queue using AMQP::Queue constructor
156
+ AMQP.start("amqp://guest:guest@dev.rabbitmq.com:5672/") do |connection, open_ok|
157
+ AMQP::Channel.new do |channel, open_ok|
158
+ AMQP::Queue.new(channel, "images.resize", :durable => true) do |queue, declare_ok|
159
+ puts "#{queue.name} is ready to go."
160
+
161
+ connection.close {
162
+ EventMachine.stop { exit }
163
+ }
164
+ end
165
+ end
166
+ end
167
+ </code>
168
+ </pre>
169
+
170
+ the same piece of code that uses {AMQP::Channel#queue} for convenience:
171
+
172
+ <pre>
173
+ <code>
174
+ #!/usr/bin/env ruby
175
+ # encoding: utf-8
176
+
177
+ require "rubygems"
178
+ require "amqp"
179
+
180
+ # Declaring a client-named queue using AMQP::Queue constructor
181
+ AMQP.start("amqp://guest:guest@dev.rabbitmq.com:5672/") do |connection, open_ok|
182
+ AMQP::Channel.new do |channel, open_ok|
183
+ channel.queue("images.resize", :durable => true) do |queue, declare_ok|
184
+ puts "#{queue.name} is ready to go."
185
+
186
+ connection.close {
187
+ EventMachine.stop { exit }
188
+ }
189
+ end
190
+ end
191
+ end
192
+ </code>
193
+ </pre>
194
+
195
+
196
+ h2. Declaring a temporary exclusive queue
197
+
198
+ To declare a server-named, exclusive, auto-deleted queue, pass "" (empty string) as queue name and
199
+ use :exclusive and :auto_delete options:
200
+
201
+ <pre>
202
+ <code>
203
+ #!/usr/bin/env ruby
204
+ # encoding: utf-8
205
+
206
+ require "rubygems"
207
+ require "amqp"
208
+
209
+ # Declaring a server-named queue using AMQP::Queue constructor
210
+ AMQP.start("amqp://guest:guest@dev.rabbitmq.com:5672/") do |connection, open_ok|
211
+ AMQP::Channel.new do |channel, open_ok|
212
+ AMQP::Queue.new(channel, "", :auto_delete => true, :exclusive => true) do |queue, declare_ok|
213
+ puts "#{queue.name} is ready to go."
214
+
215
+ connection.close {
216
+ EventMachine.stop { exit }
217
+ }
218
+ end
219
+ end
220
+ end
221
+ </code>
222
+ </pre>
223
+
224
+ the same piece of code that uses {AMQP::Channel#queue} for convenience:
225
+
226
+ <pre>
227
+ <code>
228
+ #!/usr/bin/env ruby
229
+ # encoding: utf-8
230
+
231
+ require "rubygems"
232
+ require "amqp"
233
+
234
+ # Declaring a server-named queue using AMQP::Queue constructor
235
+ AMQP.start("amqp://guest:guest@dev.rabbitmq.com:5672/") do |connection, open_ok|
236
+ AMQP::Channel.new do |channel, open_ok|
237
+ channel.queue("", :auto_delete => true, :exclusive => true) do |queue, declare_ok|
238
+ puts "#{queue.name} is ready to go."
239
+
240
+ connection.close {
241
+ EventMachine.stop { exit }
242
+ }
243
+ end
244
+ end
245
+ end
246
+ </code>
247
+ </pre>
248
+
249
+
250
+
251
+ h2. Binding queues to exchanges
252
+
253
+ In order to receive messages, a queue needs to be bound to at least one exchange. Most of the time binding is explcit (done by applications).
254
+ To bind a queue to an exchange, use {AMQP::Queue#bind). Argument can be either an {AMQP::Exchange} instance or exchange name:
255
+
256
+ <pre>
257
+ <code>
258
+ #!/usr/bin/env ruby
259
+ # encoding: utf-8
260
+
261
+ require "rubygems"
262
+ require "amqp"
263
+
264
+ # Binding a queue to an exchange
265
+ AMQP.start("amqp://guest:guest@dev.rabbitmq.com:5672/") do |connection, open_ok|
266
+ AMQP::Channel.new do |channel, open_ok|
267
+ exchange = channel.fanout("amq.fanout")
268
+
269
+ channel.queue("", :auto_delete => true, :exclusive => true) do |queue, declare_ok|
270
+ queue.bind(exchange) do |bind_ok|
271
+ puts "Just bound #{queue.name} to #{exchange.name}"
272
+ end
273
+
274
+ connection.close {
275
+ EventMachine.stop { exit }
276
+ }
277
+ end
278
+ end
279
+ end
280
+ </code>
281
+ </pre>
282
+
283
+ <pre>
284
+ <code>
285
+ #!/usr/bin/env ruby
286
+ # encoding: utf-8
287
+
288
+ require "rubygems"
289
+ require "amqp"
290
+
291
+ # Binding a queue to an exchange
292
+ AMQP.start("amqp://guest:guest@dev.rabbitmq.com:5672/") do |connection, open_ok|
293
+ AMQP::Channel.new do |channel, open_ok|
294
+ exchange_name = "amq.fanout"
295
+
296
+ channel.queue("", :auto_delete => true, :exclusive => true) do |queue, declare_ok|
297
+ queue.bind(exchange_name) do |bind_ok|
298
+ puts "Just bound #{queue.name} to #{exchange_name}"
299
+ end
300
+
301
+ connection.close {
302
+ EventMachine.stop { exit }
303
+ }
304
+ end
305
+ end
306
+ end
307
+ </code>
308
+ </pre>
309
+
310
+
311
+ h2. Subscribing to receive messages ("push API")
312
+
313
+ Each queue usually has one or more consumers (message handlers). Without it, queues are not very useful, right?
314
+ To subscribe to receive messages when they arrive to the queue ("start a queue consumer"), one uses {AMQP::Queue#subscribe} method.
315
+ Then when a message arrives, message header and body (aka payload) are passed to handling block:
316
+
317
+ <pre>
318
+ <code>
319
+ #!/usr/bin/env ruby
320
+ # encoding: utf-8
321
+
322
+ require "rubygems"
323
+ require "amqp"
324
+
325
+ AMQP.start("amqp://guest:guest@dev.rabbitmq.com:5672/") do |connection, open_ok|
326
+ AMQP::Channel.new do |channel, open_ok|
327
+ exchange = channel.fanout("amq.fanout")
328
+
329
+ channel.queue("", :auto_delete => true, :exclusive => true) do |queue, declare_ok|
330
+ queue.bind(exchange).subscribe do |headers, payload|
331
+ puts "Received a message: #{payload.inspect}. Shutting down..."
332
+
333
+ connection.close {
334
+ EM.stop { exit }
335
+ }
336
+ end
337
+
338
+ EventMachine.add_timer(0.2) do
339
+ exchange.publish("Ohai!")
340
+ end
341
+ end
342
+ end
343
+ end
344
+ </code>
345
+ </pre>
346
+
347
+ h3. Exclusive consumers
348
+
349
+ TBD
350
+
351
+
352
+
353
+ h2. Fetching messages when needed ("pull API")
354
+
355
+ AMQP 0.9.1 also provides a way for applications to fetch (pull) messages from the queue only when necessary. For that, use
356
+ {AMQP::Queue#pop}:
357
+
358
+ <pre>
359
+ <code>
360
+ #!/usr/bin/env ruby
361
+ # encoding: utf-8
362
+
363
+ require "rubygems"
364
+ require "amqp"
365
+
366
+ AMQP.start("amqp://guest:guest@dev.rabbitmq.com:5672/") do |connection, open_ok|
367
+ AMQP::Channel.new do |channel, open_ok|
368
+ exchange = channel.fanout("amq.fanout")
369
+
370
+ channel.queue("", :auto_delete => true, :exclusive => true) do |queue, declare_ok|
371
+ queue.bind(exchange) do |_|
372
+ puts "Bound. Publishing a message..."
373
+ exchange.publish("Ohai!")
374
+ end
375
+
376
+ EventMachine.add_timer(0.5) do
377
+ queue.pop do |response|
378
+ puts "Fetched a message: #{response.inspect}. Shutting down..."
379
+
380
+ connection.close {
381
+ EM.stop { exit }
382
+ }
383
+ end
384
+ end
385
+ end
386
+ end
387
+ end
388
+ </code>
389
+ </pre>
390
+
391
+
392
+ TBD
393
+
394
+
395
+ h2. Unsubscribing from messages
396
+
397
+ TBD
398
+
399
+
400
+ h2. Unbinding queues from exchanges
401
+
402
+ To unbind queue from exchange, use {AMQP::Queue#unbind}:
403
+
404
+ <pre>
405
+ <code>
406
+ #!/usr/bin/env ruby
407
+ # encoding: utf-8
408
+
409
+ require "rubygems"
410
+ require "amqp"
411
+
412
+ AMQP.start("amqp://guest:guest@dev.rabbitmq.com:5672/") do |connection, open_ok|
413
+ puts "Connected"
414
+ AMQP::Channel.new(connection) do |channel, open_ok|
415
+ puts "Opened a channel"
416
+ channel.on_error do |arg|
417
+ raise "Channel-level exception!"
418
+ end
419
+ exchange = channel.fanout("amq.fanout")
420
+
421
+ channel.queue("", :auto_delete => true, :exclusive => true) do |queue, declare_ok|
422
+ queue.bind(exchange) do |_|
423
+ puts "Bound"
424
+ end
425
+
426
+ EventMachine.add_timer(0.5) do
427
+ queue.unbind(exchange) do |_|
428
+ puts "Unbound. Shutting down..."
429
+
430
+ connection.close {
431
+ EM.stop { exit }
432
+ }
433
+ end
434
+ end # EventMachine.add_timer
435
+ end # channel.queue
436
+ end
437
+ end
438
+ </code>
439
+ </pre>
440
+
441
+ Note that unbinding an exchange queue was never bound to will result in an exception.
442
+
443
+
444
+ h2. Purging queues
445
+
446
+ It is possible to purge (remove all messages from) a queue using {AMQP::Queue#purge):
447
+
448
+ <pre>
449
+ <code>
450
+ #!/usr/bin/env ruby
451
+ # encoding: utf-8
452
+
453
+ require "rubygems"
454
+ require "amqp"
455
+
456
+ AMQP.start("amqp://guest:guest@dev.rabbitmq.com:5672/") do |connection, open_ok|
457
+ puts "Connected"
458
+ AMQP::Channel.new(connection) do |channel, open_ok|
459
+ puts "Opened a channel"
460
+ channel.on_error do |arg|
461
+ raise "Channel-level exception!"
462
+ end
463
+ exchange = channel.fanout("amq.fanout")
464
+
465
+ channel.queue("", :auto_delete => true, :exclusive => true) do |queue, declare_ok|
466
+ queue.purge do |_|
467
+ puts "Queue now has no messages"
468
+ end
469
+
470
+ EventMachine.add_timer(0.5) do
471
+ connection.close {
472
+ EM.stop { exit }
473
+ }
474
+ end # EventMachine.add_timer
475
+ end # channel.queue
476
+ end
477
+ end
478
+ </code>
479
+ </pre>
480
+
481
+ Callback is optional. However, remember that this operation takes some time.
482
+
483
+
484
+ h2. Deleting queues
485
+
486
+ To delete a queue, use {AMQP::Queue#delete}:
487
+
488
+ <pre>
489
+ <code>
490
+ #!/usr/bin/env ruby
491
+ # encoding: utf-8
492
+
493
+ require "rubygems"
494
+ require "amqp"
495
+
496
+ AMQP.start("amqp://guest:guest@dev.rabbitmq.com:5672/") do |connection, open_ok|
497
+ puts "Connected"
498
+ AMQP::Channel.new(connection) do |channel, open_ok|
499
+ puts "Opened a channel"
500
+ channel.on_error do |arg|
501
+ raise "Channel-level exception!"
502
+ end
503
+ exchange = channel.fanout("amq.fanout")
504
+
505
+ channel.queue("", :auto_delete => true, :exclusive => true) do |queue, declare_ok|
506
+ EventMachine.add_timer(0.5) do
507
+ queue.delete do
508
+ puts "Deleted a queue"
509
+ connection.close {
510
+ EM.stop { exit }
511
+ }
512
+ end
513
+ end # EventMachine.add_timer
514
+ end # channel.queue
515
+ end
516
+ end
517
+ </code>
518
+ </pre>
519
+
520
+ Callback can be omitted. However, remember that this operation takes some time.
521
+
522
+ h2. Queue durability vs Message durability
523
+
524
+ TBD
525
+
526
+
527
+
528
+ h2. Error handling and recovery
16
529
 
17
530
  TBD
18
531
 
19
532
 
20
533
 
534
+ h2. Vendor-specific extensions related to queues
535
+
536
+ TBD
537
+
538
+
539
+
540
+ h2. What to read next
541
+
542
+ TBD
543
+
544
+
21
545
  h2. Tell us what you think!
22
546
 
23
547
  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: