bunny 0.9.0.pre6 → 0.9.0.pre7

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore CHANGED
@@ -19,3 +19,4 @@ playground/*
19
19
  repl-*
20
20
  debug/*
21
21
  *.dump
22
+ deploy.docs.sh
@@ -1,3 +1,59 @@
1
+ ## Changes between Bunny 0.9.0.pre6 and 0.9.0.pre7
2
+
3
+ ### Bunny::Channel#on_error
4
+
5
+ `Bunny::Channel#on_error` is a new method that lets you define
6
+ handlers for channel errors that are caused by methods that have no
7
+ responses in the protocol (`basic.ack`, `basic.reject`, and `basic.nack`).
8
+
9
+ This is rarely necessary but helps make sure no error goes unnoticed.
10
+
11
+ Example:
12
+
13
+ ``` ruby
14
+ channel.on_error |ch, channel_close|
15
+ puts channel_close.inspect
16
+ end
17
+ ```
18
+
19
+ ### Fixed Framing of Larger Messages With Unicode Characters
20
+
21
+ Larger (over 128K) messages with non-ASCII characters are now always encoded
22
+ correctly with amq-protocol `1.2.0`.
23
+
24
+
25
+ ### Efficiency Improvements
26
+
27
+ Publishing of large messages is now done more efficiently.
28
+
29
+ Contributed by Greg Brockman.
30
+
31
+
32
+ ### API Reference
33
+
34
+ [Bunny API reference](http://reference.rubybunny.info) is now up online.
35
+
36
+
37
+ ### Bunny::Channel#basic_publish Support For :persistent
38
+
39
+ `Bunny::Channel#basic_publish` now supports both
40
+ `:delivery_mode` and `:persistent` options.
41
+
42
+ ### Bunny::Channel#nacked_set
43
+
44
+ `Bunny::Channel#nacked_set` is a counter-part to `Bunny::Channel#unacked_set`
45
+ that contains `basic.nack`-ed (rejected) delivery tags.
46
+
47
+
48
+ ### Single-threaded Network Activity Mode
49
+
50
+ Passing `:threaded => false` to `Bunny.new` now will use the same
51
+ thread for publisher confirmations (may be useful for retry logic
52
+ implementation).
53
+
54
+ Contributed by Greg Brockman.
55
+
56
+
1
57
  ## Changes between Bunny 0.9.0.pre5 and 0.9.0.pre6
2
58
 
3
59
  ### Automatic Network Failure Recovery
data/Gemfile CHANGED
@@ -24,6 +24,11 @@ gem "SystemTimer", "1.2", :platform => :ruby_18
24
24
  gem "rake"
25
25
  gem "effin_utf8"
26
26
 
27
+ group :development do
28
+ gem "yard"
29
+ gem "redcarpet"
30
+ end
31
+
27
32
  group :test do
28
33
  gem "rspec", "~> 2.8.0"
29
34
  end
data/README.md CHANGED
@@ -1,4 +1,4 @@
1
- # About Bunny
1
+ # Bunny, a Ruby RabbitMQ Client
2
2
 
3
3
  Bunny is a synchronous RabbitMQ client that focuses on ease of use. With the next
4
4
  0.9 release (currently in master), it is feature complete, supports all RabbitMQ 3.0
@@ -13,8 +13,7 @@ Bunny 0.9 and more recent versions support Ruby 1.9.3, 1.9.2, JRuby 1.7, Rubiniu
13
13
  ## Supported RabbitMQ Versions
14
14
 
15
15
  Bunny `0.8.x` and later versions only support RabbitMQ 2.x and 3.x.
16
-
17
- Bunny versions `0.7.x` and earlier support RabbitMQ 1.x and 2.x.
16
+ Bunny `0.7.x` and earlier versions support RabbitMQ 1.x and 2.x.
18
17
 
19
18
 
20
19
  ## Changes in Bunny 0.9
@@ -77,7 +76,7 @@ conn.stop
77
76
  ```
78
77
 
79
78
 
80
- ## Community & Getting Help
79
+ ## Documentation
81
80
 
82
81
  ### Getting Started
83
82
 
@@ -89,9 +88,10 @@ Other documentation guides are available at [rubybunny.info](http://rubybunny.in
89
88
 
90
89
  ### API Reference
91
90
 
92
- [Bunny API Reference](http://rubydoc.info/github/ruby-amqp/bunny/master/frames) is available at rubydoc.info.
91
+ [Bunny API Reference](http://reference.rubybunny.info/).
92
+
93
93
 
94
- ## Getting Help
94
+ ## Community and Getting Help
95
95
 
96
96
  ### Mailing List
97
97
 
@@ -128,6 +128,12 @@ First, clone the repository and run
128
128
 
129
129
  bundle install --binstubs
130
130
 
131
+ then set up RabbitMQ vhosts with
132
+
133
+ ./bin/ci/before_build.sh
134
+
135
+ (if needed, set `RABBITMQCTL` env variable to point to `rabbitmqctl` you want to use)
136
+
131
137
  and then run tests with
132
138
 
133
139
  ./bin/rspec -cfs spec
@@ -29,7 +29,7 @@ Gem::Specification.new do |s|
29
29
  map { |mail| Base64.decode64(mail) }
30
30
 
31
31
  # Dependencies
32
- s.add_dependency "amq-protocol", ">= 1.0.1"
32
+ s.add_dependency "amq-protocol", ">= 1.2.0"
33
33
 
34
34
  # Files.
35
35
  s.has_rdoc = true
@@ -0,0 +1,34 @@
1
+ #!/usr/bin/env ruby
2
+ # encoding: utf-8
3
+
4
+ require "bundler"
5
+ Bundler.setup
6
+
7
+ $:.unshift(File.expand_path("../../../lib", __FILE__))
8
+
9
+ require 'bunny'
10
+
11
+ conn = Bunny.new(:heartbeat_interval => 8)
12
+ conn.start
13
+
14
+ ch = conn.create_channel
15
+ x = ch.topic("bunny.examples.recovery.topic", :durable => false)
16
+ q = ch.queue("bunny.examples.recovery.client_named_queue2", :durable => true)
17
+ q.purge
18
+
19
+ q.bind(x, :routing_key => "abc").bind(x, :routing_key => "def")
20
+
21
+ loop do
22
+ sleep 1.5
23
+ body = rand.to_s
24
+ puts "Published #{body}"
25
+ x.publish(body, :routing_key => ["abc", "def"].sample)
26
+
27
+ sleep 1.5
28
+ _, _, payload = q.pop
29
+ if payload
30
+ puts "Consumed #{payload}"
31
+ else
32
+ puts "Consumed nothing"
33
+ end
34
+ end
@@ -22,7 +22,10 @@ q.subscribe do |delivery_info, metadata, payload|
22
22
  end
23
23
 
24
24
  loop do
25
- sleep 3
26
- puts "Tick"
27
- x.publish(rand.to_s, :routing_key => ["abc", "def", "ghi", "xyz"].sample)
25
+ sleep 2
26
+ data = rand.to_s
27
+ rk = ["abc", "def"].sample
28
+
29
+ puts "Published #{data}, routing key: #{rk}"
30
+ x.publish(data, :routing_key => rk)
28
31
  end
@@ -22,7 +22,10 @@ q.subscribe do |delivery_info, metadata, payload|
22
22
  end
23
23
 
24
24
  loop do
25
- sleep 3
26
- puts "Tick"
27
- x.publish(rand.to_s, :routing_key => ["abc", "def", "ghi", "xyz"].sample)
25
+ sleep 2
26
+ data = rand.to_s
27
+ rk = ["abc", "def"].sample
28
+
29
+ puts "Published #{data}, routing key: #{rk}"
30
+ x.publish(data, :routing_key => rk)
28
31
  end
@@ -1,3 +1,4 @@
1
+ # -*- coding: utf-8 -*-
1
2
  require "thread"
2
3
  require "set"
3
4
 
@@ -11,16 +12,145 @@ require "bunny/return_info"
11
12
  require "bunny/message_properties"
12
13
 
13
14
  module Bunny
15
+ # ## What are AMQP channels
16
+ #
17
+ # To quote {http://www.rabbitmq.com/resources/specs/amqp0-9-1.pdf AMQP 0.9.1 specification}:
18
+ #
19
+ # AMQP 0.9.1 is a multi-channelled protocol. Channels provide a way to multiplex
20
+ # a heavyweight TCP/IP connection into several light weight connections.
21
+ # This makes the protocol more “firewall friendly” since port usage is predictable.
22
+ # It also means that traffic shaping and other network QoS features can be easily employed.
23
+ # Channels are independent of each other and can perform different functions simultaneously
24
+ # with other channels, the available bandwidth being shared between the concurrent activities.
25
+ #
26
+ #
27
+ # ## Opening Channels
28
+ #
29
+ # Channels can be opened either via `Bunny::Session#create_channel` (sufficient in the majority
30
+ # of cases) or by instantiating `Bunny::Channel` directly:
31
+ #
32
+ # @example Using {Bunny::Session#create_channel}:
33
+ # conn = Bunny.new
34
+ # conn.start
35
+ #
36
+ # ch = conn.create_channel
37
+ #
38
+ # This will automatically allocate channel id.
39
+ #
40
+ # @example Instantiating
41
+ #
42
+ # ## Closing Channels
43
+ #
44
+ # Channels are closed via {Bunny::Channel#close}. Channels that get a channel-level exception are
45
+ # closed, too. Closed channels can no longer be used. Attempts to use them will raise
46
+ # {Bunny::ChannelAlreadyClosed}.
47
+ #
48
+ # @example
49
+ #
50
+ # ch = conn.create_channel
51
+ # ch.close
52
+ #
53
+ # ## Higher-level API
54
+ #
55
+ # Bunny offers two sets of methods on {Bunny::Channel}: known as higher-level and lower-level
56
+ # APIs, respectively. Higher-level API mimics {http://rubyamqp.info amqp gem} API where
57
+ # exchanges and queues are objects (instance of {Bunny::Exchange} and {Bunny::Queue}, respectively).
58
+ # Lower-level API is built around AMQP 0.9.1 methods (commands), where queues and exchanges are
59
+ # passed as strings (à la RabbitMQ Java client, {http://clojurerabbitmq.info Langohr} and Pika).
60
+ #
61
+ # ### Queue Operations In Higher-level API
62
+ #
63
+ # * {Bunny::Channel#queue} is used to declare queues. The rest of the API is in {Bunny::Queue}.
64
+ #
65
+ #
66
+ # ### Exchange Operations In Higher-level API
67
+ #
68
+ # * {Bunny::Channel#topic} declares a topic exchange. The rest of the API is in {Bunny::Exchange}.
69
+ # * {Bunny::Channel#direct} declares a direct exchange.
70
+ # * {Bunny::Channel#fanout} declares a fanout exchange.
71
+ # * {Bunny::Channel#headers} declares a headers exchange.
72
+ # * {Bunny::Channel#default_exchange}
73
+ # * {Bunny::Channel#exchange} is used to declare exchanges with type specified as a symbol or string.
74
+ #
75
+ #
76
+ # ## Channel Qos (Prefetch Level)
77
+ #
78
+ # It is possible to control how many messages at most a consumer will be given (before it acknowledges
79
+ # or rejects previously consumed ones). This setting is per channel and controlled via {Bunny::Channel#prefetch}.
80
+ #
81
+ #
82
+ # ## Channel IDs
83
+ #
84
+ # Channels are identified by their ids which are integers. Bunny takes care of allocating and
85
+ # releasing them as channels are opened and closed. It is almost never necessary to specify
86
+ # channel ids explicitly.
87
+ #
88
+ # There is a limit on the maximum number of channels per connection, usually 65536. Note
89
+ # that allocating channels is very cheap on both client and server so having tens, hundreds
90
+ # or even thousands of channels is not a problem.
91
+ #
92
+ # ## Channels and Error Handling
93
+ #
94
+ # Channel-level exceptions are more common than connection-level ones and often indicate
95
+ # issues applications can recover from (such as consuming from or trying to delete
96
+ # a queue that does not exist).
97
+ #
98
+ # With Bunny, channel-level exceptions are raised as Ruby exceptions, for example,
99
+ # {Bunny::NotFound}, that provide access to the underlying `channel.close` method
100
+ # information.
101
+ #
102
+ # @example Handling 404 NOT_FOUND
103
+ # begin
104
+ # ch.queue_delete("queue_that_should_not_exist#{rand}")
105
+ # rescue Bunny::NotFound => e
106
+ # puts "Channel-level exception! Code: #{e.channel_close.reply_code}, message: #{e.channel_close.reply_text}"
107
+ # end
108
+ #
109
+ # @example Handling 406 PRECONDITION_FAILED
110
+ # begin
111
+ # ch2 = conn.create_channel
112
+ # q = "bunny.examples.recovery.q#{rand}"
113
+ #
114
+ # ch2.queue_declare(q, :durable => false)
115
+ # ch2.queue_declare(q, :durable => true)
116
+ # rescue Bunny::PreconditionFailed => e
117
+ # puts "Channel-level exception! Code: #{e.channel_close.reply_code}, message: #{e.channel_close.reply_text}"
118
+ # ensure
119
+ # conn.create_channel.queue_delete(q)
120
+ # end
121
+ #
122
+ # @see http://www.rabbitmq.com/tutorials/amqp-concepts.html AMQP 0.9.1 Model Concepts Guide
123
+ # @see http://rubybunny.info/articles/getting_started.html Getting Started with RabbitMQ Using Bunny
124
+ # @see http://rubybunny.info/articles/error_handling.html Error Handling and Recovery Guide
14
125
  class Channel
15
126
 
16
127
  #
17
128
  # API
18
129
  #
19
-
20
- attr_accessor :id, :connection, :status, :work_pool
21
- attr_reader :next_publish_seq_no, :queues, :exchanges, :unconfirmed_set, :consumers
22
-
23
-
130
+ # @return [Integer] Channel id
131
+ attr_accessor :id
132
+ # @return [Bunny::Session] AMQP connection this channel was opened on
133
+ attr_reader :connection
134
+ attr_reader :status
135
+ # @return [Bunny::ConsumerWorkPool] Thread pool delivered messages are dispatched to.
136
+ attr_reader :work_pool
137
+ # @return [Integer] Next publisher confirmations sequence index
138
+ attr_reader :next_publish_seq_no
139
+ # @return [Hash<String, Bunny::Queue>] Queue instances declared on this channel
140
+ attr_reader :queues
141
+ # @return [Hash<String, Bunny::Exchange>] Exchange instances declared on this channel
142
+ attr_reader :exchanges
143
+ # @return [Set<Integer>] Set of published message indexes that are currently unconfirmed
144
+ attr_reader :unconfirmed_set
145
+ # @return [Set<Integer>] Set of nacked message indexes that have been nacked
146
+ attr_reader :nacked_set
147
+ # @return [Hash<String, Bunny::Consumer>] Consumer instances declared on this channel
148
+ attr_reader :consumers
149
+
150
+
151
+ # @param [Bunny::Session] connection AMQP 0.9.1 connection
152
+ # @param [Integer] id Channel id, pass nil to make Bunny automatically allocate it
153
+ # @param [Bunny::ConsumerWorkPool] work_pool Thread pool for delivery processing, by default of size 1
24
154
  def initialize(connection = nil, id = nil, work_pool = ConsumerWorkPool.new(1))
25
155
  @connection = connection
26
156
  @id = id || @connection.next_channel_id
@@ -39,13 +169,31 @@ module Bunny
39
169
 
40
170
  @unconfirmed_set_mutex = Mutex.new
41
171
 
42
- @continuations = ::Queue.new
43
- @confirms_continuations = ::Queue.new
172
+ @continuations = ::Queue.new
173
+ @confirms_continuations = ::Queue.new
174
+ @basic_get_continuations = ::Queue.new
175
+ # threads awaiting on continuations. Used to unblock
176
+ # them when network connection goes down so that busy loops
177
+ # that perform synchronous operations can work. MK.
178
+ @threads_waiting_on_continuations = Set.new
179
+ @threads_waiting_on_confirms_continuations = Set.new
180
+ @threads_waiting_on_basic_get_continuations = Set.new
44
181
 
45
182
  @next_publish_seq_no = 0
46
183
  end
47
184
 
185
+ def read_write_timeout
186
+ @connection.read_write_timeout
187
+ end
188
+
189
+ # Opens the channel and resets its internal state
190
+ # @return [Bunny::Channel] Self
191
+ # @api public
48
192
  def open
193
+ @threads_waiting_on_continuations = Set.new
194
+ @threads_waiting_on_confirms_continuations = Set.new
195
+ @threads_waiting_on_basic_get_continuations = Set.new
196
+
49
197
  @connection.open_channel(self)
50
198
  # clear last channel error
51
199
  @last_channel_error = nil
@@ -55,116 +203,290 @@ module Bunny
55
203
  self
56
204
  end
57
205
 
206
+ # Closes the channel. Closed channels can no longer be used (this includes associated
207
+ # {Bunny::Queue}, {Bunny::Exchange} and {Bunny::Consumer} instances.
208
+ # @api public
58
209
  def close
59
210
  @connection.close_channel(self)
60
211
  closed!
61
212
  end
62
213
 
214
+ # @return [Boolean] true if this channel is open, false otherwise
215
+ # @api public
63
216
  def open?
64
217
  @status == :open
65
218
  end
66
219
 
220
+ # @return [Boolean] true if this channel is closed (manually or because of an exception), false otherwise
221
+ # @api public
67
222
  def closed?
68
223
  @status == :closed
69
224
  end
70
225
 
71
- def queue(name = AMQ::Protocol::EMPTY_STRING, opts = {})
72
- q = find_queue(name) || Bunny::Queue.new(self, name, opts)
73
-
74
- register_queue(q)
75
- end
76
-
77
226
 
78
227
  #
79
- # Backwards compatibility with 0.8.0
228
+ # @group Backwards compatibility with 0.8.0
80
229
  #
81
230
 
231
+ # @return [Integer] Channel id
82
232
  def number
83
233
  self.id
84
234
  end
85
235
 
236
+ # @return [Boolean] true if this channel is open
86
237
  def active
87
- @active
238
+ open?
88
239
  end
89
240
 
241
+ # @return [Bunny::Session] Connection this channel was opened on
90
242
  def client
91
243
  @connection
92
244
  end
93
245
 
246
+ # @private
94
247
  def frame_size
95
248
  @connection.frame_max
96
249
  end
97
250
 
251
+ # @endgroup
252
+
98
253
 
99
254
  #
100
255
  # Higher-level API, similar to amqp gem
101
256
  #
102
257
 
258
+ # @group Higher-level API for exchange operations
259
+
260
+ # Declares a fanout exchange or looks it up in the cache of previously
261
+ # declared exchanges.
262
+ #
263
+ # @param [String] name Exchange name
264
+ # @param [Hash] opts Exchange parameters
265
+ #
266
+ # @option opts [Boolean] :durable (false) Should the exchange be durable?
267
+ # @option opts [Boolean] :auto_delete (false) Should the exchange be automatically deleted when no longer in use?
268
+ # @option opts [Hash] :arguments ({}) Optional exchange arguments (used by RabbitMQ extensions)
269
+ #
270
+ # @return [Bunny::Exchange] Exchange instance
271
+ # @see http://rubybunny.info/articles/exchanges.html Exchanges and Publishing guide
272
+ # @see http://rubybunny.info/articles/extensions.html RabbitMQ Extensions to AMQP 0.9.1 guide
273
+ # @api public
103
274
  def fanout(name, opts = {})
104
275
  Exchange.new(self, :fanout, name, opts)
105
276
  end
106
277
 
278
+ # Declares a direct exchange or looks it up in the cache of previously
279
+ # declared exchanges.
280
+ #
281
+ # @param [String] name Exchange name
282
+ # @param [Hash] opts Exchange parameters
283
+ #
284
+ # @option opts [Boolean] :durable (false) Should the exchange be durable?
285
+ # @option opts [Boolean] :auto_delete (false) Should the exchange be automatically deleted when no longer in use?
286
+ # @option opts [Hash] :arguments ({}) Optional exchange arguments (used by RabbitMQ extensions)
287
+ #
288
+ # @return [Bunny::Exchange] Exchange instance
289
+ # @see http://rubybunny.info/articles/exchanges.html Exchanges and Publishing guide
290
+ # @see http://rubybunny.info/articles/extensions.html RabbitMQ Extensions to AMQP 0.9.1 guide
291
+ # @api public
107
292
  def direct(name, opts = {})
108
293
  Exchange.new(self, :direct, name, opts)
109
294
  end
110
295
 
296
+ # Declares a topic exchange or looks it up in the cache of previously
297
+ # declared exchanges.
298
+ #
299
+ # @param [String] name Exchange name
300
+ # @param [Hash] opts Exchange parameters
301
+ #
302
+ # @option opts [Boolean] :durable (false) Should the exchange be durable?
303
+ # @option opts [Boolean] :auto_delete (false) Should the exchange be automatically deleted when no longer in use?
304
+ # @option opts [Hash] :arguments ({}) Optional exchange arguments (used by RabbitMQ extensions)
305
+ #
306
+ # @return [Bunny::Exchange] Exchange instance
307
+ # @see http://rubybunny.info/articles/exchanges.html Exchanges and Publishing guide
308
+ # @see http://rubybunny.info/articles/extensions.html RabbitMQ Extensions to AMQP 0.9.1 guide
309
+ # @api public
111
310
  def topic(name, opts = {})
112
311
  Exchange.new(self, :topic, name, opts)
113
312
  end
114
313
 
314
+ # Declares a headers exchange or looks it up in the cache of previously
315
+ # declared exchanges.
316
+ #
317
+ # @param [String] name Exchange name
318
+ # @param [Hash] opts Exchange parameters
319
+ #
320
+ # @option opts [Boolean] :durable (false) Should the exchange be durable?
321
+ # @option opts [Boolean] :auto_delete (false) Should the exchange be automatically deleted when no longer in use?
322
+ # @option opts [Hash] :arguments ({}) Optional exchange arguments
323
+ #
324
+ # @return [Bunny::Exchange] Exchange instance
325
+ # @see http://rubybunny.info/articles/exchanges.html Exchanges and Publishing guide
326
+ # @see http://rubybunny.info/articles/extensions.html RabbitMQ Extensions to AMQP 0.9.1 guide
327
+ # @api public
115
328
  def headers(name, opts = {})
116
329
  Exchange.new(self, :headers, name, opts)
117
330
  end
118
331
 
332
+ # Provides access to the default exchange
333
+ # @see http://rubybunny.info/articles/exchanges.html Exchanges and Publishing guide
334
+ # @api public
119
335
  def default_exchange
120
336
  self.direct(AMQ::Protocol::EMPTY_STRING, :no_declare => true)
121
337
  end
122
338
 
339
+ # Declares a headers exchange or looks it up in the cache of previously
340
+ # declared exchanges.
341
+ #
342
+ # @param [String] name Exchange name
343
+ # @param [Hash] opts Exchange parameters
344
+ #
345
+ # @option opts [Boolean] :durable (false) Should the exchange be durable?
346
+ # @option opts [Boolean] :auto_delete (false) Should the exchange be automatically deleted when no longer in use?
347
+ # @option opts [Hash] :arguments ({}) Optional exchange arguments
348
+ #
349
+ # @return [Bunny::Exchange] Exchange instance
350
+ # @see http://rubybunny.info/articles/exchanges.html Exchanges and Publishing guide
351
+ # @see http://rubybunny.info/articles/extensions.html RabbitMQ Extensions to AMQP 0.9.1 guide
123
352
  def exchange(name, opts = {})
124
353
  Exchange.new(self, opts.fetch(:type, :direct), name, opts)
125
354
  end
126
355
 
356
+ # @endgroup
357
+
358
+
359
+ # @group Higher-level API for queue operations
360
+
361
+ # Declares an exchange or looks it up in the per-channel cache.
362
+ #
363
+ # @param [String] name Queue name. Pass an empty string to declare a server-named queue (make RabbitMQ generate a unique name).
364
+ # @param [Hash] opts Queue properties and other options
365
+ #
366
+ # @option options [Boolean] :durable (false) Should this queue be durable?
367
+ # @option options [Boolean] :auto-delete (false) Should this queue be automatically deleted when the last consumer disconnects?
368
+ # @option options [Boolean] :exclusive (false) Should this queue be exclusive (only can be used by this connection, removed when the connection is closed)?
369
+ # @option options [Boolean] :arguments ({}) Additional optional arguments (typically used by RabbitMQ extensions and plugins)
370
+ #
371
+ # @return [Bunny::Queue] Queue that was declared or looked up in the cache
372
+ # @see http://rubybunny.info/articles/queues.html Queues and Consumers guide
373
+ # @see http://rubybunny.info/articles/extensions.html RabbitMQ Extensions guide
374
+ # @api public
375
+ def queue(name = AMQ::Protocol::EMPTY_STRING, opts = {})
376
+ q = find_queue(name) || Bunny::Queue.new(self, name, opts)
377
+
378
+ register_queue(q)
379
+ end
380
+
381
+ # @endgroup
382
+
383
+
384
+ # @group QoS and Flow Control
385
+
386
+ # Sets how many messages will be given to consumers on this channel before they
387
+ # have to acknowledge or reject one of the previously consumed messages
388
+ #
389
+ # @param [Integer] prefetch_count Prefetch (QoS setting) for this channel
390
+ # @see http://rubybunny.info/articles/exchanges.html Exchanges and Publishing guide
391
+ # @see http://rubybunny.info/articles/queues.html Queues and Consumers guide
392
+ # @api public
127
393
  def prefetch(prefetch_count)
128
394
  self.basic_qos(prefetch_count, false)
129
395
  end
130
396
 
397
+ # Flow control. When set to false, RabbitMQ will stop delivering messages on this
398
+ # channel.
399
+ #
400
+ # @param [Boolean] active Should messages to consumers on this channel be delivered?
401
+ # @api public
131
402
  def flow(active)
132
403
  channel_flow(active)
133
404
  end
134
405
 
406
+ # Tells RabbitMQ to redeliver unacknowledged messages
407
+ # @api public
135
408
  def recover(ignored = true)
136
409
  # RabbitMQ only supports basic.recover with requeue = true
137
410
  basic_recover(true)
138
411
  end
139
412
 
413
+ # @endgroup
414
+
415
+
416
+
417
+ # @group Message acknowledgements
418
+
419
+ # Rejects a message. A rejected message can be requeued or
420
+ # dropped by RabbitMQ.
421
+ #
422
+ # @param [Integer] delivery_tag Delivery tag to reject
423
+ # @param [Boolean] requeue Should this message be requeued instead of dropping it?
424
+ # @see Bunny::Channel#ack
425
+ # @see Bunny::Channel#nack
426
+ # @see http://rubybunny.info/articles/queues.html Queues and Consumers guide
427
+ # @api public
140
428
  def reject(delivery_tag, requeue = false)
141
429
  basic_reject(delivery_tag, requeue)
142
430
  end
143
431
 
432
+ # Acknowledges a message. Acknowledged messages are completely removed from the queue.
433
+ #
434
+ # @param [Integer] delivery_tag Delivery tag to acknowledge
435
+ # @param [Boolean] multiple (false) Should all unacknowledged messages up to this be acknowledged as well?
436
+ # @see Bunny::Channel#nack
437
+ # @see http://rubybunny.info/articles/queues.html Queues and Consumers guide
438
+ # @api public
144
439
  def ack(delivery_tag, multiple = false)
145
440
  basic_ack(delivery_tag, multiple)
146
441
  end
147
442
  alias acknowledge ack
148
443
 
444
+ # Rejects a message. A rejected message can be requeued or
445
+ # dropped by RabbitMQ. This method is similar to {Bunny::Channel#reject} but
446
+ # supports rejecting multiple messages at once, and is usually preferred.
447
+ #
448
+ # @param [Integer] delivery_tag Delivery tag to reject
449
+ # @param [Boolean] requeue Should this message be requeued instead of dropping it?
450
+ # @param [Boolean] multiple (false) Should all unacknowledged messages up to this be rejected as well?
451
+ # @see Bunny::Channel#ack
452
+ # @see http://rubybunny.info/articles/queues.html Queues and Consumers guide
453
+ # @api public
149
454
  def nack(delivery_tag, requeue, multiple = false)
150
455
  basic_nack(delivery_tag, requeue, multiple)
151
456
  end
152
457
 
153
- def on_error(&block)
154
- @default_error_handler = block
155
- end
156
-
157
- def using_publisher_confirmations?
158
- @next_publish_seq_no > 0
159
- end
458
+ # @endgroup
160
459
 
161
460
  #
162
461
  # Lower-level API, exposes protocol operations as they are defined in the protocol,
163
462
  # without any OO sugar on top, by design.
164
463
  #
165
464
 
166
- # basic.*
465
+ # @group Consumer and Message operations (basic.*)
167
466
 
467
+ # Publishes a message using basic.publish AMQP 0.9.1 method.
468
+ #
469
+ # @param [String] payload Message payload. It will never be modified by Bunny or RabbitMQ in any way.
470
+ # @param [String] exchange Exchange to publish to
471
+ # @param [String] routing_key Routing key
472
+ # @param [Hash] opts Publishing options
473
+ #
474
+ # @option opts [Boolean] :persistent Should the message be persisted to disk?
475
+ # @option opts [Boolean] :mandatory Should the message be returned if it cannot be routed to any queue?
476
+ # @option opts [Integer] :timestamp A timestamp associated with this message
477
+ # @option opts [Integer] :expiration Expiration time after which the message will be deleted
478
+ # @option opts [String] :type Message type, e.g. what type of event or command this message represents. Can be any string
479
+ # @option opts [String] :reply_to Queue name other apps should send the response to
480
+ # @option opts [String] :content_type Message content type (e.g. application/json)
481
+ # @option opts [String] :content_encoding Message content encoding (e.g. gzip)
482
+ # @option opts [String] :correlation_id Message correlated to this one, e.g. what request this message is a reply for
483
+ # @option opts [Integer] :priority Message priority, 0 to 9. Not used by RabbitMQ, only applications
484
+ # @option opts [String] :message_id Any message identifier
485
+ # @option opts [String] :user_id Optional user ID. Verified by RabbitMQ against the actual connection username
486
+ # @option opts [String] :app_id Optional application ID
487
+ #
488
+ # @return [Bunny::Channel] Self
489
+ # @api public
168
490
  def basic_publish(payload, exchange, routing_key, opts = {})
169
491
  raise_if_no_longer_open!
170
492
 
@@ -174,7 +496,12 @@ module Bunny
174
496
  exchange
175
497
  end
176
498
 
177
- meta = { :priority => 0, :delivery_mode => 2, :content_type => "application/octet-stream" }.
499
+ mode = if opts.fetch(:persistent, true)
500
+ 2
501
+ else
502
+ 1
503
+ end
504
+ meta = { :priority => 0, :delivery_mode => mode, :content_type => "application/octet-stream" }.
178
505
  merge(opts)
179
506
 
180
507
  if @next_publish_seq_no > 0
@@ -194,24 +521,65 @@ module Bunny
194
521
  self
195
522
  end
196
523
 
524
+ # Synchronously fetches a message from the queue, if there are any. This method is
525
+ # for cases when the convenience of synchronous operations is more important than
526
+ # throughput.
527
+ #
528
+ # @param [String] queue Queue name
529
+ # @param [Hash] opts Options
530
+ #
531
+ # @option opts [Boolean] :ack (true) Will this message be acknowledged manually?
532
+ #
533
+ # @return [Array] A triple of delivery info, message properties and message content
534
+ #
535
+ # @example Using Bunny::Channel#basic_get with manual acknowledgements
536
+ # conn = Bunny.new
537
+ # conn.start
538
+ # ch = conn.create_channel
539
+ # # here we assume the queue already exists and has messages
540
+ # delivery_info, properties, payload = ch.basic_get("bunny.examples.queue1", :ack => true)
541
+ # ch.acknowledge(delivery_info.delivery_tag)
542
+ # @see Bunny::Queue#pop
543
+ # @see http://rubybunny.info/articles/queues.html Queues and Consumers guide
544
+ # @api public
197
545
  def basic_get(queue, opts = {:ack => true})
198
546
  raise_if_no_longer_open!
199
547
 
200
- @connection.send_frame(AMQ::Protocol::Basic::Get.encode(@id, queue, !opts[:ack]))
201
- @last_basic_get_response = @continuations.pop
548
+ @connection.send_frame(AMQ::Protocol::Basic::Get.encode(@id, queue, !(opts[:ack])))
549
+ # this is a workaround for the edge case when basic_get is called in a tight loop
550
+ # and network goes down we need to perform recovery. The problem is, basic_get will
551
+ # keep blocking the thread that calls it without clear way to constantly unblock it
552
+ # from the network activity loop (where recovery happens) with the current continuations
553
+ # implementation (and even more correct and convenient ones, such as wait/notify, should
554
+ # we implement them). So we return a triple of nils immediately which apps should be
555
+ # able to handle anyway as "got no message, no need to act". MK.
556
+ @last_basic_get_response = if @connection.open?
557
+ wait_on_basic_get_continuations
558
+ else
559
+ [nil, nil, nil]
560
+ end
202
561
 
203
562
  raise_if_continuation_resulted_in_a_channel_error!
204
563
  @last_basic_get_response
205
564
  end
206
565
 
566
+ # Controls message delivery rate using basic.qos AMQP 0.9.1 method.
567
+ #
568
+ # @param [Integer] prefetch_count How many messages can consumers on this channel be given at a time
569
+ # (before they have to acknowledge or reject one of the earlier received messages)
570
+ # @param [Boolean] global (false) Ignored, as it is not supported by RabbitMQ
571
+ # @return [AMQ::Protocol::Basic::QosOk] RabbitMQ response
572
+ # @see Bunny::Channel#prefetch
573
+ # @see http://rubybunny.info/articles/queues.html Queues and Consumers guide
574
+ # @api public
207
575
  def basic_qos(prefetch_count, global = false)
208
576
  raise ArgumentError.new("prefetch count must be a positive integer, given: #{prefetch_count}") if prefetch_count < 0
209
577
  raise_if_no_longer_open!
210
578
 
211
579
  @connection.send_frame(AMQ::Protocol::Basic::Qos.encode(@id, 0, prefetch_count, global))
212
580
 
213
- Bunny::Timer.timeout(1, ClientTimeout) do
214
- @last_basic_qos_ok = @continuations.pop
581
+ Bunny::Timer.timeout(read_write_timeout, ClientTimeout) do
582
+ @last_basic_qos_ok = wait_on_continuations
215
583
  end
216
584
  raise_if_continuation_resulted_in_a_channel_error!
217
585
 
@@ -220,18 +588,61 @@ module Bunny
220
588
  @last_basic_qos_ok
221
589
  end
222
590
 
591
+ # Redeliver unacknowledged messages
592
+ #
593
+ # @param [Boolean] requeue Should messages be requeued?
594
+ # @return [AMQ::Protocol::Basic::RecoverOk] RabbitMQ response
595
+ # @api public
223
596
  def basic_recover(requeue)
224
597
  raise_if_no_longer_open!
225
598
 
226
599
  @connection.send_frame(AMQ::Protocol::Basic::Recover.encode(@id, requeue))
227
- Bunny::Timer.timeout(1, ClientTimeout) do
228
- @last_basic_recover_ok = @continuations.pop
600
+ Bunny::Timer.timeout(read_write_timeout, ClientTimeout) do
601
+ @last_basic_recover_ok = wait_on_continuations
229
602
  end
230
603
  raise_if_continuation_resulted_in_a_channel_error!
231
604
 
232
605
  @last_basic_recover_ok
233
606
  end
234
607
 
608
+ # Rejects or requeues a message.
609
+ #
610
+ # @param [Integer] delivery_tag Delivery tag obtained from delivery info
611
+ # @param [Boolean] requeue Should the message be requeued?
612
+ # @return [NilClass] nil
613
+ #
614
+ # @example Requeue a message
615
+ # conn = Bunny.new
616
+ # conn.start
617
+ #
618
+ # ch = conn.create_channel
619
+ # q.subscribe do |delivery_info, properties, payload|
620
+ # # requeue the message
621
+ # ch.basic_reject(delivery_info.delivery_tag, true)
622
+ # end
623
+ #
624
+ # @example Reject a message
625
+ # conn = Bunny.new
626
+ # conn.start
627
+ #
628
+ # ch = conn.create_channel
629
+ # q.subscribe do |delivery_info, properties, payload|
630
+ # # requeue the message
631
+ # ch.basic_reject(delivery_info.delivery_tag, false)
632
+ # end
633
+ #
634
+ # @example Requeue a message fetched via basic.get
635
+ # conn = Bunny.new
636
+ # conn.start
637
+ #
638
+ # ch = conn.create_channel
639
+ # # we assume the queue exists and has messages
640
+ # delivery_info, properties, payload = ch.basic_get("bunny.examples.queue3", :ack => true)
641
+ # ch.basic_reject(delivery_info.delivery_tag, true)
642
+ #
643
+ # @see Bunny::Channel#basic_nack
644
+ # @see http://rubybunny.info/articles/queues.html Queues and Consumers guide
645
+ # @api public
235
646
  def basic_reject(delivery_tag, requeue)
236
647
  raise_if_no_longer_open!
237
648
  @connection.send_frame(AMQ::Protocol::Basic::Reject.encode(@id, delivery_tag, requeue))
@@ -239,6 +650,45 @@ module Bunny
239
650
  nil
240
651
  end
241
652
 
653
+ # Acknowledges a delivery (message).
654
+ #
655
+ # @param [Integer] delivery_tag Delivery tag obtained from delivery info
656
+ # @param [Boolean] multiple Should all deliveries up to this one be acknowledged?
657
+ # @return [NilClass] nil
658
+ #
659
+ # @example Ack a message
660
+ # conn = Bunny.new
661
+ # conn.start
662
+ #
663
+ # ch = conn.create_channel
664
+ # q.subscribe do |delivery_info, properties, payload|
665
+ # # requeue the message
666
+ # ch.basic_ack(delivery_info.delivery_tag)
667
+ # end
668
+ #
669
+ # @example Ack a message fetched via basic.get
670
+ # conn = Bunny.new
671
+ # conn.start
672
+ #
673
+ # ch = conn.create_channel
674
+ # # we assume the queue exists and has messages
675
+ # delivery_info, properties, payload = ch.basic_get("bunny.examples.queue3", :ack => true)
676
+ # ch.basic_ack(delivery_info.delivery_tag)
677
+ #
678
+ # @example Ack multiple messages fetched via basic.get
679
+ # conn = Bunny.new
680
+ # conn.start
681
+ #
682
+ # ch = conn.create_channel
683
+ # # we assume the queue exists and has messages
684
+ # _, _, payload1 = ch.basic_get("bunny.examples.queue3", :ack => true)
685
+ # _, _, payload2 = ch.basic_get("bunny.examples.queue3", :ack => true)
686
+ # delivery_info, properties, payload3 = ch.basic_get("bunny.examples.queue3", :ack => true)
687
+ # # ack all fetched messages up to payload3
688
+ # ch.basic_ack(delivery_info.delivery_tag, true)
689
+ #
690
+ # @see http://rubybunny.info/articles/queues.html Queues and Consumers guide
691
+ # @api public
242
692
  def basic_ack(delivery_tag, multiple)
243
693
  raise_if_no_longer_open!
244
694
  @connection.send_frame(AMQ::Protocol::Basic::Ack.encode(@id, delivery_tag, multiple))
@@ -246,6 +696,59 @@ module Bunny
246
696
  nil
247
697
  end
248
698
 
699
+ # Rejects or requeues messages just like {Bunny::Channel#basic_reject} but can do so
700
+ # with multiple messages at once.
701
+ #
702
+ # @param [Integer] delivery_tag Delivery tag obtained from delivery info
703
+ # @param [Boolean] requeue Should the message be requeued?
704
+ # @param [Boolean] multiple Should all deliveries up to this one be rejected/requeued?
705
+ # @return [NilClass] nil
706
+ #
707
+ # @example Requeue a message
708
+ # conn = Bunny.new
709
+ # conn.start
710
+ #
711
+ # ch = conn.create_channel
712
+ # q.subscribe do |delivery_info, properties, payload|
713
+ # # requeue the message
714
+ # ch.basic_nack(delivery_info.delivery_tag, true)
715
+ # end
716
+ #
717
+ # @example Reject a message
718
+ # conn = Bunny.new
719
+ # conn.start
720
+ #
721
+ # ch = conn.create_channel
722
+ # q.subscribe do |delivery_info, properties, payload|
723
+ # # requeue the message
724
+ # ch.basic_nack(delivery_info.delivery_tag, false)
725
+ # end
726
+ #
727
+ # @example Requeue a message fetched via basic.get
728
+ # conn = Bunny.new
729
+ # conn.start
730
+ #
731
+ # ch = conn.create_channel
732
+ # # we assume the queue exists and has messages
733
+ # delivery_info, properties, payload = ch.basic_get("bunny.examples.queue3", :ack => true)
734
+ # ch.basic_nack(delivery_info.delivery_tag, true)
735
+ #
736
+ #
737
+ # @example Requeue multiple messages fetched via basic.get
738
+ # conn = Bunny.new
739
+ # conn.start
740
+ #
741
+ # ch = conn.create_channel
742
+ # # we assume the queue exists and has messages
743
+ # _, _, payload1 = ch.basic_get("bunny.examples.queue3", :ack => true)
744
+ # _, _, payload2 = ch.basic_get("bunny.examples.queue3", :ack => true)
745
+ # delivery_info, properties, payload3 = ch.basic_get("bunny.examples.queue3", :ack => true)
746
+ # # requeue all fetched messages up to payload3
747
+ # ch.basic_nack(delivery_info.delivery_tag, true, true)
748
+ #
749
+ # @see http://rubybunny.info/articles/queues.html Queues and Consumers guide
750
+ # @see http://rubybunny.info/articles/extensions.html RabbitMQ Extensions guide
751
+ # @api public
249
752
  def basic_nack(delivery_tag, requeue, multiple = false)
250
753
  raise_if_no_longer_open!
251
754
  @connection.send_frame(AMQ::Protocol::Basic::Nack.encode(@id,
@@ -256,6 +759,19 @@ module Bunny
256
759
  nil
257
760
  end
258
761
 
762
+ # Registers a consumer for queue. Delivered messages will be handled with the block
763
+ # provided to this method.
764
+ #
765
+ # @param [String, Bunny::Queue] queue Queue to consume from
766
+ # @param [String] consumer_tag Consumer tag (unique identifier), generated by Bunny by default
767
+ # @param [Boolean] no_ack (false) If false, delivered messages will be automatically acknowledged.
768
+ # If true, manual acknowledgements will be necessary.
769
+ # @param [Boolean] exclusive (false) Should this consumer be exclusive?
770
+ # @param [Hash] arguments (nil) Optional arguments that may be used by RabbitMQ extensions, etc
771
+ #
772
+ # @return [AMQ::Protocol::Basic::ConsumeOk] RabbitMQ response
773
+ # @see http://rubybunny.info/articles/queues.html Queues and Consumers guide
774
+ # @api public
259
775
  def basic_consume(queue, consumer_tag = generate_consumer_tag, no_ack = false, exclusive = false, arguments = nil, &block)
260
776
  raise_if_no_longer_open!
261
777
  maybe_start_consumer_work_pool!
@@ -280,8 +796,8 @@ module Bunny
280
796
  add_consumer(queue_name, consumer_tag, no_ack, exclusive, arguments, &block)
281
797
  end
282
798
 
283
- Bunny::Timer.timeout(1, ClientTimeout) do
284
- @last_basic_consume_ok = @continuations.pop
799
+ Bunny::Timer.timeout(read_write_timeout, ClientTimeout) do
800
+ @last_basic_consume_ok = wait_on_continuations
285
801
  end
286
802
  # covers server-generated consumer tags
287
803
  add_consumer(queue_name, @last_basic_consume_ok.consumer_tag, no_ack, exclusive, arguments, &block)
@@ -289,6 +805,14 @@ module Bunny
289
805
  @last_basic_consume_ok
290
806
  end
291
807
 
808
+ # Registers a consumer for queue as {Bunny::Consumer} instance.
809
+ #
810
+ # @param [Bunny::Consumer] consumer Consumer to register. It should already have queue name, consumer tag
811
+ # and other attributes set.
812
+ #
813
+ # @return [AMQ::Protocol::Basic::ConsumeOk] RabbitMQ response
814
+ # @see http://rubybunny.info/articles/queues.html Queues and Consumers guide
815
+ # @api public
292
816
  def basic_consume_with(consumer)
293
817
  raise_if_no_longer_open!
294
818
  maybe_start_consumer_work_pool!
@@ -308,8 +832,8 @@ module Bunny
308
832
  register_consumer(consumer.consumer_tag, consumer)
309
833
  end
310
834
 
311
- Bunny::Timer.timeout(1, ClientTimeout) do
312
- @last_basic_consume_ok = @continuations.pop
835
+ Bunny::Timer.timeout(read_write_timeout, ClientTimeout) do
836
+ @last_basic_consume_ok = wait_on_continuations
313
837
  end
314
838
  # covers server-generated consumer tags
315
839
  register_consumer(@last_basic_consume_ok.consumer_tag, consumer)
@@ -319,19 +843,46 @@ module Bunny
319
843
  @last_basic_consume_ok
320
844
  end
321
845
 
846
+ # Removes a consumer. Messages for this consumer will no longer be delivered. If the queue
847
+ # it was on is auto-deleted and this consumer was the last one, the queue will be deleted.
848
+ #
849
+ # @param [String] consumer_tag Consumer tag (unique identifier) to cancel
850
+ #
851
+ # @return [AMQ::Protocol::Basic::CancelOk] RabbitMQ response
852
+ # @see http://rubybunny.info/articles/queues.html Queues and Consumers guide
853
+ # @api public
322
854
  def basic_cancel(consumer_tag)
323
855
  @connection.send_frame(AMQ::Protocol::Basic::Cancel.encode(@id, consumer_tag, false))
324
856
 
325
- Bunny::Timer.timeout(1, ClientTimeout) do
326
- @last_basic_cancel_ok = @continuations.pop
857
+ Bunny::Timer.timeout(read_write_timeout, ClientTimeout) do
858
+ @last_basic_cancel_ok = wait_on_continuations
327
859
  end
328
860
 
329
861
  @last_basic_cancel_ok
330
862
  end
331
863
 
864
+ # @endgroup
332
865
 
333
- # queue.*
334
866
 
867
+ # @group Queue operations (queue.*)
868
+
869
+ # Declares a queue using queue.declare AMQP 0.9.1 method.
870
+ #
871
+ # @param [String] name Queue name
872
+ # @param [Hash] opts Queue properties
873
+ #
874
+ # @option opts [Boolean] durable (false) Should information about this queue be persisted to disk so that it
875
+ # can survive broker restarts? Typically set to true for long-lived queues.
876
+ # @option opts [Boolean] auto_delete (false) Should this queue be deleted when the last consumer is cancelled?
877
+ # @option opts [Boolean] exclusive (false) Should only this connection be able to use this queue?
878
+ # If true, the queue will be automatically deleted when this
879
+ # connection is closed
880
+ # @option opts [Boolean] passive (false) If true, queue will be checked for existence. If it does not
881
+ # exist, {Bunny::NotFound} will be raised.
882
+ #
883
+ # @return [AMQ::Protocol::Queue::DeclareOk] RabbitMQ response
884
+ # @see http://rubybunny.info/articles/queues.html Queues and Consumers guide
885
+ # @api public
335
886
  def queue_declare(name, opts = {})
336
887
  raise_if_no_longer_open!
337
888
 
@@ -343,13 +894,24 @@ module Bunny
343
894
  opts.fetch(:auto_delete, false),
344
895
  false,
345
896
  opts[:arguments]))
346
- @last_queue_declare_ok = @continuations.pop
897
+ @last_queue_declare_ok = wait_on_continuations
347
898
 
348
899
  raise_if_continuation_resulted_in_a_channel_error!
349
900
 
350
901
  @last_queue_declare_ok
351
902
  end
352
903
 
904
+ # Deletes a queue using queue.delete AMQP 0.9.1 method
905
+ #
906
+ # @param [String] name Queue name
907
+ # @param [Hash] opts Options
908
+ #
909
+ # @option opts [Boolean] if_unused (false) Should this queue be deleted only if it has no consumers?
910
+ # @option opts [Boolean] if_empty (false) Should this queue be deleted only if it has no messages?
911
+ #
912
+ # @return [AMQ::Protocol::Queue::DeleteOk] RabbitMQ response
913
+ # @see http://rubybunny.info/articles/queues.html Queues and Consumers guide
914
+ # @api public
353
915
  def queue_delete(name, opts = {})
354
916
  raise_if_no_longer_open!
355
917
 
@@ -358,27 +920,47 @@ module Bunny
358
920
  opts[:if_unused],
359
921
  opts[:if_empty],
360
922
  false))
361
- Bunny::Timer.timeout(1, ClientTimeout) do
362
- @last_queue_delete_ok = @continuations.pop
923
+ Bunny::Timer.timeout(read_write_timeout, ClientTimeout) do
924
+ @last_queue_delete_ok = wait_on_continuations
363
925
  end
364
926
  raise_if_continuation_resulted_in_a_channel_error!
365
927
 
366
928
  @last_queue_delete_ok
367
929
  end
368
930
 
931
+ # Purges a queue (removes all messages from it) using queue.purge AMQP 0.9.1 method.
932
+ #
933
+ # @param [String] name Queue name
934
+ #
935
+ # @return [AMQ::Protocol::Queue::PurgeOk] RabbitMQ response
936
+ # @see http://rubybunny.info/articles/queues.html Queues and Consumers guide
937
+ # @api public
369
938
  def queue_purge(name, opts = {})
370
939
  raise_if_no_longer_open!
371
940
 
372
941
  @connection.send_frame(AMQ::Protocol::Queue::Purge.encode(@id, name, false))
373
942
 
374
- Bunny::Timer.timeout(1, ClientTimeout) do
375
- @last_queue_purge_ok = @continuations.pop
943
+ Bunny::Timer.timeout(read_write_timeout, ClientTimeout) do
944
+ @last_queue_purge_ok = wait_on_continuations
376
945
  end
377
946
  raise_if_continuation_resulted_in_a_channel_error!
378
947
 
379
948
  @last_queue_purge_ok
380
949
  end
381
950
 
951
+ # Binds a queue to an exchange using queue.bind AMQP 0.9.1 method
952
+ #
953
+ # @param [String] name Queue name
954
+ # @param [String] exchange Exchange name
955
+ # @param [Hash] opts Options
956
+ #
957
+ # @option opts [String] routing_key (nil) Routing key used for binding
958
+ # @option opts [Hash] arguments ({}) Optional arguments
959
+ #
960
+ # @return [AMQ::Protocol::Queue::BindOk] RabbitMQ response
961
+ # @see http://rubybunny.info/articles/queues.html Queues and Consumers guide
962
+ # @see http://rubybunny.info/articles/bindings.html Bindings guide
963
+ # @api public
382
964
  def queue_bind(name, exchange, opts = {})
383
965
  raise_if_no_longer_open!
384
966
 
@@ -394,14 +976,27 @@ module Bunny
394
976
  opts[:routing_key],
395
977
  false,
396
978
  opts[:arguments]))
397
- Bunny::Timer.timeout(1, ClientTimeout) do
398
- @last_queue_bind_ok = @continuations.pop
979
+ Bunny::Timer.timeout(read_write_timeout, ClientTimeout) do
980
+ @last_queue_bind_ok = wait_on_continuations
399
981
  end
400
982
 
401
983
  raise_if_continuation_resulted_in_a_channel_error!
402
984
  @last_queue_bind_ok
403
985
  end
404
986
 
987
+ # Unbinds a queue from an exchange using queue.unbind AMQP 0.9.1 method
988
+ #
989
+ # @param [String] name Queue name
990
+ # @param [String] exchange Exchange name
991
+ # @param [Hash] opts Options
992
+ #
993
+ # @option opts [String] routing_key (nil) Routing key used for binding
994
+ # @option opts [Hash] arguments ({}) Optional arguments
995
+ #
996
+ # @return [AMQ::Protocol::Queue::UnbindOk] RabbitMQ response
997
+ # @see http://rubybunny.info/articles/queues.html Queues and Consumers guide
998
+ # @see http://rubybunny.info/articles/bindings.html Bindings guide
999
+ # @api public
405
1000
  def queue_unbind(name, exchange, opts = {})
406
1001
  raise_if_no_longer_open!
407
1002
 
@@ -416,17 +1011,33 @@ module Bunny
416
1011
  exchange_name,
417
1012
  opts[:routing_key],
418
1013
  opts[:arguments]))
419
- Bunny::Timer.timeout(1, ClientTimeout) do
420
- @last_queue_unbind_ok = @continuations.pop
1014
+ Bunny::Timer.timeout(read_write_timeout, ClientTimeout) do
1015
+ @last_queue_unbind_ok = wait_on_continuations
421
1016
  end
422
1017
 
423
1018
  raise_if_continuation_resulted_in_a_channel_error!
424
1019
  @last_queue_unbind_ok
425
1020
  end
426
1021
 
1022
+ # @endgroup
1023
+
427
1024
 
428
- # exchange.*
1025
+ # @group Exchange operations (exchange.*)
429
1026
 
1027
+ # Declares a echange using echange.declare AMQP 0.9.1 method.
1028
+ #
1029
+ # @param [String] name Exchange name
1030
+ # @param [Hash] opts Exchange properties
1031
+ #
1032
+ # @option opts [Boolean] durable (false) Should information about this echange be persisted to disk so that it
1033
+ # can survive broker restarts? Typically set to true for long-lived exchanges.
1034
+ # @option opts [Boolean] auto_delete (false) Should this echange be deleted when it is no longer used?
1035
+ # @option opts [Boolean] passive (false) If true, exchange will be checked for existence. If it does not
1036
+ # exist, {Bunny::NotFound} will be raised.
1037
+ #
1038
+ # @return [AMQ::Protocol::Exchange::DeclareOk] RabbitMQ response
1039
+ # @see http://rubybunny.info/articles/echanges.html Exchanges and Publishing guide
1040
+ # @api public
430
1041
  def exchange_declare(name, type, opts = {})
431
1042
  raise_if_no_longer_open!
432
1043
 
@@ -439,14 +1050,24 @@ module Bunny
439
1050
  false,
440
1051
  false,
441
1052
  opts[:arguments]))
442
- Bunny::Timer.timeout(1, ClientTimeout) do
443
- @last_exchange_declare_ok = @continuations.pop
1053
+ Bunny::Timer.timeout(read_write_timeout, ClientTimeout) do
1054
+ @last_exchange_declare_ok = wait_on_continuations
444
1055
  end
445
1056
 
446
1057
  raise_if_continuation_resulted_in_a_channel_error!
447
1058
  @last_exchange_declare_ok
448
1059
  end
449
1060
 
1061
+ # Deletes a exchange using exchange.delete AMQP 0.9.1 method
1062
+ #
1063
+ # @param [String] name Exchange name
1064
+ # @param [Hash] opts Options
1065
+ #
1066
+ # @option opts [Boolean] if_unused (false) Should this exchange be deleted only if it is no longer used
1067
+ #
1068
+ # @return [AMQ::Protocol::Exchange::DeleteOk] RabbitMQ response
1069
+ # @see http://rubybunny.info/articles/exchanges.html Exchanges and Publishing guide
1070
+ # @api public
450
1071
  def exchange_delete(name, opts = {})
451
1072
  raise_if_no_longer_open!
452
1073
 
@@ -454,14 +1075,29 @@ module Bunny
454
1075
  name,
455
1076
  opts[:if_unused],
456
1077
  false))
457
- Bunny::Timer.timeout(1, ClientTimeout) do
458
- @last_exchange_delete_ok = @continuations.pop
1078
+ Bunny::Timer.timeout(read_write_timeout, ClientTimeout) do
1079
+ @last_exchange_delete_ok = wait_on_continuations
459
1080
  end
460
1081
 
461
1082
  raise_if_continuation_resulted_in_a_channel_error!
462
1083
  @last_exchange_delete_ok
463
1084
  end
464
1085
 
1086
+ # Binds an exchange to another exchange using exchange.bind AMQP 0.9.1 extension
1087
+ # that RabbitMQ provides.
1088
+ #
1089
+ # @param [String] source Source exchange name
1090
+ # @param [String] destination Destination exchange name
1091
+ # @param [Hash] opts Options
1092
+ #
1093
+ # @option opts [String] routing_key (nil) Routing key used for binding
1094
+ # @option opts [Hash] arguments ({}) Optional arguments
1095
+ #
1096
+ # @return [AMQ::Protocol::Exchange::BindOk] RabbitMQ response
1097
+ # @see http://rubybunny.info/articles/exchanges.html Exchanges and Publishing guide
1098
+ # @see http://rubybunny.info/articles/bindings.html Bindings guide
1099
+ # @see http://rubybunny.info/articles/extensions.html RabbitMQ Extensions guide
1100
+ # @api public
465
1101
  def exchange_bind(source, destination, opts = {})
466
1102
  raise_if_no_longer_open!
467
1103
 
@@ -483,14 +1119,29 @@ module Bunny
483
1119
  opts[:routing_key],
484
1120
  false,
485
1121
  opts[:arguments]))
486
- Bunny::Timer.timeout(1, ClientTimeout) do
487
- @last_exchange_bind_ok = @continuations.pop
1122
+ Bunny::Timer.timeout(read_write_timeout, ClientTimeout) do
1123
+ @last_exchange_bind_ok = wait_on_continuations
488
1124
  end
489
1125
 
490
1126
  raise_if_continuation_resulted_in_a_channel_error!
491
1127
  @last_exchange_bind_ok
492
1128
  end
493
1129
 
1130
+ # Unbinds an exchange from another exchange using exchange.unbind AMQP 0.9.1 extension
1131
+ # that RabbitMQ provides.
1132
+ #
1133
+ # @param [String] source Source exchange name
1134
+ # @param [String] destination Destination exchange name
1135
+ # @param [Hash] opts Options
1136
+ #
1137
+ # @option opts [String] routing_key (nil) Routing key used for binding
1138
+ # @option opts [Hash] arguments ({}) Optional arguments
1139
+ #
1140
+ # @return [AMQ::Protocol::Exchange::UnbindOk] RabbitMQ response
1141
+ # @see http://rubybunny.info/articles/exchanges.html Exchanges and Publishing guide
1142
+ # @see http://rubybunny.info/articles/bindings.html Bindings guide
1143
+ # @see http://rubybunny.info/articles/extensions.html RabbitMQ Extensions guide
1144
+ # @api public
494
1145
  def exchange_unbind(source, destination, opts = {})
495
1146
  raise_if_no_longer_open!
496
1147
 
@@ -512,101 +1163,196 @@ module Bunny
512
1163
  opts[:routing_key],
513
1164
  false,
514
1165
  opts[:arguments]))
515
- Bunny::Timer.timeout(1, ClientTimeout) do
516
- @last_exchange_unbind_ok = @continuations.pop
1166
+ Bunny::Timer.timeout(read_write_timeout, ClientTimeout) do
1167
+ @last_exchange_unbind_ok = wait_on_continuations
517
1168
  end
518
1169
 
519
1170
  raise_if_continuation_resulted_in_a_channel_error!
520
1171
  @last_exchange_unbind_ok
521
1172
  end
522
1173
 
523
- # channel.*
1174
+ # @endgroup
1175
+
1176
+
524
1177
 
1178
+ # @group Flow control (channel.*)
1179
+
1180
+ # Enables or disables message flow for the channel. When message flow is disabled,
1181
+ # no new messages will be delivered to consumers on this channel. This is typically
1182
+ # used by consumers that cannot keep up with the influx of messages.
1183
+ #
1184
+ # @note Recent (e.g. 2.8.x., 3.x) RabbitMQ will employ TCP/IP-level back pressure on publishers if it detects
1185
+ # that consumers do not keep up with them.
1186
+ #
1187
+ # @return [AMQ::Protocol::Channel::FlowOk] RabbitMQ response
1188
+ # @see http://rubybunny.info/articles/queues.html Queues and Consumers guide
1189
+ # @api public
525
1190
  def channel_flow(active)
526
1191
  raise_if_no_longer_open!
527
1192
 
528
1193
  @connection.send_frame(AMQ::Protocol::Channel::Flow.encode(@id, active))
529
- Bunny::Timer.timeout(1, ClientTimeout) do
530
- @last_channel_flow_ok = @continuations.pop
1194
+ Bunny::Timer.timeout(read_write_timeout, ClientTimeout) do
1195
+ @last_channel_flow_ok = wait_on_continuations
531
1196
  end
532
1197
  raise_if_continuation_resulted_in_a_channel_error!
533
1198
 
534
1199
  @last_channel_flow_ok
535
1200
  end
536
1201
 
537
- # tx.*
1202
+ # @endgroup
538
1203
 
1204
+
1205
+
1206
+ # @group Transactions (tx.*)
1207
+
1208
+ # Puts the channel into transaction mode (starts a transaction)
1209
+ # @return [AMQ::Protocol::Tx::SelectOk] RabbitMQ response
1210
+ # @api public
539
1211
  def tx_select
540
1212
  raise_if_no_longer_open!
541
1213
 
542
1214
  @connection.send_frame(AMQ::Protocol::Tx::Select.encode(@id))
543
- Bunny::Timer.timeout(1, ClientTimeout) do
544
- @last_tx_select_ok = @continuations.pop
1215
+ Bunny::Timer.timeout(read_write_timeout, ClientTimeout) do
1216
+ @last_tx_select_ok = wait_on_continuations
545
1217
  end
546
1218
  raise_if_continuation_resulted_in_a_channel_error!
547
1219
 
548
1220
  @last_tx_select_ok
549
1221
  end
550
1222
 
1223
+ # Commits current transaction
1224
+ # @return [AMQ::Protocol::Tx::CommitOk] RabbitMQ response
1225
+ # @api public
551
1226
  def tx_commit
552
1227
  raise_if_no_longer_open!
553
1228
 
554
1229
  @connection.send_frame(AMQ::Protocol::Tx::Commit.encode(@id))
555
- Bunny::Timer.timeout(1, ClientTimeout) do
556
- @last_tx_commit_ok = @continuations.pop
1230
+ Bunny::Timer.timeout(read_write_timeout, ClientTimeout) do
1231
+ @last_tx_commit_ok = wait_on_continuations
557
1232
  end
558
1233
  raise_if_continuation_resulted_in_a_channel_error!
559
1234
 
560
1235
  @last_tx_commit_ok
561
1236
  end
562
1237
 
1238
+ # Rolls back current transaction
1239
+ # @return [AMQ::Protocol::Tx::RollbackOk] RabbitMQ response
1240
+ # @api public
563
1241
  def tx_rollback
564
1242
  raise_if_no_longer_open!
565
1243
 
566
1244
  @connection.send_frame(AMQ::Protocol::Tx::Rollback.encode(@id))
567
- Bunny::Timer.timeout(1, ClientTimeout) do
568
- @last_tx_rollback_ok = @continuations.pop
1245
+ Bunny::Timer.timeout(read_write_timeout, ClientTimeout) do
1246
+ @last_tx_rollback_ok = wait_on_continuations
569
1247
  end
570
1248
  raise_if_continuation_resulted_in_a_channel_error!
571
1249
 
572
1250
  @last_tx_rollback_ok
573
1251
  end
574
1252
 
575
- # confirm.*
1253
+ # @endgroup
1254
+
1255
+
1256
+
1257
+ # @group Publisher Confirms (confirm.*)
1258
+
1259
+ # @return [Boolean] true if this channel has Publisher Confirms enabled, false otherwise
1260
+ # @api public
1261
+ def using_publisher_confirmations?
1262
+ @next_publish_seq_no > 0
1263
+ end
576
1264
 
1265
+ # Enables publisher confirms for the channel.
1266
+ # @return [AMQ::Protocol::Confirm::SelectOk] RabbitMQ response
1267
+ # @see #wait_for_confirms
1268
+ # @see #unconfirmed_set
1269
+ # @see #nacked_set
1270
+ # @see http://rubybunny.info/articles/extensions.html RabbitMQ Extensions guide
1271
+ # @api public
577
1272
  def confirm_select(callback=nil)
578
1273
  raise_if_no_longer_open!
579
1274
 
580
1275
  if @next_publish_seq_no == 0
581
1276
  @confirms_continuations = ::Queue.new
582
1277
  @unconfirmed_set = Set.new
1278
+ @nacked_set = Set.new
583
1279
  @next_publish_seq_no = 1
584
1280
  end
585
1281
 
586
1282
  @confirms_callback = callback
587
1283
 
588
1284
  @connection.send_frame(AMQ::Protocol::Confirm::Select.encode(@id, false))
589
- Bunny::Timer.timeout(1, ClientTimeout) do
590
- @last_confirm_select_ok = @continuations.pop
1285
+ Bunny::Timer.timeout(read_write_timeout, ClientTimeout) do
1286
+ @last_confirm_select_ok = wait_on_continuations
591
1287
  end
592
1288
  raise_if_continuation_resulted_in_a_channel_error!
593
1289
  @last_confirm_select_ok
594
1290
  end
595
1291
 
1292
+ # Blocks calling thread until confirms are received for all
1293
+ # currently unacknowledged published messages.
1294
+ #
1295
+ # @return [Boolean] true if all messages were acknowledged positively, false otherwise
1296
+ # @see #confirm_select
1297
+ # @see #unconfirmed_set
1298
+ # @see #nacked_set
1299
+ # @see http://rubybunny.info/articles/extensions.html RabbitMQ Extensions guide
1300
+ # @api public
596
1301
  def wait_for_confirms
597
- @only_acks_received = true
598
- @confirms_continuations.pop
1302
+ wait_on_confirms_continuations
599
1303
 
600
1304
  @only_acks_received
601
1305
  end
602
1306
 
1307
+ # @endgroup
1308
+
1309
+
1310
+ # @group Misc
1311
+
1312
+ # Synchronizes given block using this channel's mutex.
1313
+ # @api public
1314
+ def synchronize(&block)
1315
+ @publishing_mutex.synchronize(&block)
1316
+ end
1317
+
1318
+ # Unique string supposed to be used as a consumer tag.
1319
+ #
1320
+ # @return [String] Unique string.
1321
+ # @api plugin
1322
+ def generate_consumer_tag(name = "bunny")
1323
+ "#{name}-#{Time.now.to_i * 1000}-#{Kernel.rand(999_999_999_999)}"
1324
+ end
1325
+
1326
+ # @endgroup
1327
+
1328
+
1329
+ #
1330
+ # Error Handilng
1331
+ #
1332
+
1333
+ # Defines a handler for errors that are not responses to a particular
1334
+ # operations (e.g. basic.ack, basic.reject, basic.nack).
1335
+ #
1336
+ # @api public
1337
+ def on_error(&block)
1338
+ @on_error = block
1339
+ end
1340
+
603
1341
 
604
1342
  #
605
1343
  # Recovery
606
1344
  #
607
1345
 
1346
+ # @group Network Failure Recovery
1347
+
1348
+ # Recovers basic.qos setting, exchanges, queues and consumers. Used by the Automatic Network Failure
1349
+ # Recovery feature.
1350
+ #
1351
+ # @api plugin
608
1352
  def recover_from_network_failure
609
- # puts "Recovering channel #{@id} from network failure..."
1353
+ # puts "Recovering channel #{@id}"
1354
+ release_all_continuations
1355
+
610
1356
  recover_prefetch_setting
611
1357
  recover_exchanges
612
1358
  # this includes recovering bindings
@@ -614,22 +1360,39 @@ module Bunny
614
1360
  recover_consumers
615
1361
  end
616
1362
 
1363
+ # Recovers basic.qos setting. Used by the Automatic Network Failure
1364
+ # Recovery feature.
1365
+ #
1366
+ # @api plugin
617
1367
  def recover_prefetch_setting
618
1368
  basic_qos(@prefetch_count) if @prefetch_count
619
1369
  end
620
1370
 
1371
+ # Recovers exchanges. Used by the Automatic Network Failure
1372
+ # Recovery feature.
1373
+ #
1374
+ # @api plugin
621
1375
  def recover_exchanges
622
1376
  @exchanges.values.dup.each do |x|
623
1377
  x.recover_from_network_failure
624
1378
  end
625
1379
  end
626
1380
 
1381
+ # Recovers queues and bindings. Used by the Automatic Network Failure
1382
+ # Recovery feature.
1383
+ #
1384
+ # @api plugin
627
1385
  def recover_queues
628
1386
  @queues.values.dup.each do |q|
1387
+ # puts "Recovering queue #{q.name}"
629
1388
  q.recover_from_network_failure
630
1389
  end
631
1390
  end
632
1391
 
1392
+ # Recovers consumers. Used by the Automatic Network Failure
1393
+ # Recovery feature.
1394
+ #
1395
+ # @api plugin
633
1396
  def recover_consumers
634
1397
  unless @consumers.empty?
635
1398
  @work_pool = ConsumerWorkPool.new(@work_pool.size)
@@ -640,18 +1403,21 @@ module Bunny
640
1403
  end
641
1404
  end
642
1405
 
1406
+ # @endgroup
643
1407
 
644
1408
 
645
1409
  #
646
1410
  # Implementation
647
1411
  #
648
1412
 
1413
+ # @private
649
1414
  def register_consumer(consumer_tag, consumer)
650
1415
  @consumer_mutex.synchronize do
651
1416
  @consumers[consumer_tag] = consumer
652
1417
  end
653
1418
  end
654
1419
 
1420
+ # @private
655
1421
  def add_consumer(queue, consumer_tag, no_ack, exclusive, arguments, &block)
656
1422
  @consumer_mutex.synchronize do
657
1423
  c = Consumer.new(self, queue, consumer_tag, no_ack, exclusive, arguments)
@@ -660,6 +1426,7 @@ module Bunny
660
1426
  end
661
1427
  end
662
1428
 
1429
+ # @private
663
1430
  def handle_method(method)
664
1431
  # puts "Channel#handle_frame on channel #{@id}: #{method.inspect}"
665
1432
  case method
@@ -709,12 +1476,17 @@ module Bunny
709
1476
  when AMQ::Protocol::Basic::Nack then
710
1477
  handle_ack_or_nack(method.delivery_tag, method.multiple, true)
711
1478
  when AMQ::Protocol::Channel::Close then
712
- # puts "Exception on channel #{@id}: #{method.reply_code} #{method.reply_text}"
713
1479
  closed!
714
1480
  @connection.send_frame(AMQ::Protocol::Channel::CloseOk.encode(@id))
715
1481
 
716
- @last_channel_error = instantiate_channel_level_exception(method)
717
- @continuations.push(method)
1482
+ # basic.ack, basic.reject, basic.nack. MK.
1483
+ if channel_level_exception_after_operation_that_has_no_response?(method)
1484
+ @on_error.call(self, method) if @on_error
1485
+ else
1486
+ @last_channel_error = instantiate_channel_level_exception(method)
1487
+ @continuations.push(method)
1488
+ end
1489
+
718
1490
  when AMQ::Protocol::Channel::CloseOk then
719
1491
  @continuations.push(method)
720
1492
  else
@@ -722,14 +1494,22 @@ module Bunny
722
1494
  end
723
1495
  end
724
1496
 
1497
+ # @private
1498
+ def channel_level_exception_after_operation_that_has_no_response?(method)
1499
+ method.reply_code == 406 && method.reply_text =~ /unknown delivery tag/
1500
+ end
1501
+
1502
+ # @private
725
1503
  def handle_basic_get_ok(basic_get_ok, properties, content)
726
- @continuations.push([basic_get_ok, properties, content])
1504
+ @basic_get_continuations.push([basic_get_ok, properties, content])
727
1505
  end
728
1506
 
1507
+ # @private
729
1508
  def handle_basic_get_empty(basic_get_empty)
730
- @continuations.push([nil, nil, nil])
1509
+ @basic_get_continuations.push([nil, nil, nil])
731
1510
  end
732
1511
 
1512
+ # @private
733
1513
  def handle_frameset(basic_deliver, properties, content)
734
1514
  consumer = @consumers[basic_deliver.consumer_tag]
735
1515
  if consumer
@@ -742,6 +1522,7 @@ module Bunny
742
1522
  end
743
1523
  end
744
1524
 
1525
+ # @private
745
1526
  def handle_basic_return(basic_return, properties, content)
746
1527
  x = find_exchange(basic_return.exchange)
747
1528
 
@@ -752,7 +1533,18 @@ module Bunny
752
1533
  end
753
1534
  end
754
1535
 
1536
+ # @private
755
1537
  def handle_ack_or_nack(delivery_tag, multiple, nack)
1538
+ if nack
1539
+ cloned_set = @unconfirmed_set.clone
1540
+ if multiple
1541
+ cloned_set.keep_if { |i| i <= delivery_tag }
1542
+ @nacked_set.merge(cloned_set)
1543
+ else
1544
+ @nacked_set.add(delivery_tag)
1545
+ end
1546
+ end
1547
+
756
1548
  if multiple
757
1549
  @unconfirmed_set.delete_if { |i| i <= delivery_tag }
758
1550
  else
@@ -768,75 +1560,149 @@ module Bunny
768
1560
  end
769
1561
  end
770
1562
 
1563
+ # @private
1564
+ def wait_on_continuations
1565
+ if @connection.threaded
1566
+ t = Thread.current
1567
+ @threads_waiting_on_continuations << t
1568
+
1569
+ v = @continuations.pop
1570
+ @threads_waiting_on_continuations.delete(t)
1571
+
1572
+ v
1573
+ else
1574
+ connection.event_loop.run_once until @continuations.length > 0
1575
+
1576
+ @continuations.pop
1577
+ end
1578
+ end
1579
+
1580
+ # @private
1581
+ def wait_on_basic_get_continuations
1582
+ if @connection.threaded
1583
+ t = Thread.current
1584
+ @threads_waiting_on_basic_get_continuations << t
1585
+
1586
+ v = @basic_get_continuations.pop
1587
+ @threads_waiting_on_basic_get_continuations.delete(t)
1588
+
1589
+ v
1590
+ else
1591
+ connection.event_loop.run_once until @basic_get_continuations.length > 0
1592
+
1593
+ @basic_get_continuations.pop
1594
+ end
1595
+ end
1596
+
1597
+ # @private
1598
+ def wait_on_confirms_continuations
1599
+ if @connection.threaded
1600
+ t = Thread.current
1601
+ @threads_waiting_on_confirms_continuations << t
1602
+
1603
+ v = @confirms_continuations.pop
1604
+ @threads_waiting_on_confirms_continuations.delete(t)
1605
+
1606
+ v
1607
+ else
1608
+ connection.event_loop.run_once until @confirms_continuations.length > 0
1609
+
1610
+ @confirms_continuations.pop
1611
+ end
1612
+ end
1613
+
1614
+ # Releases all continuations. Used by automatic network recovery.
1615
+ # @private
1616
+ def release_all_continuations
1617
+ if @confirms_continuations.num_waiting > 0
1618
+ @threads_waiting_on_confirms_continuations.each do |t|
1619
+ t.run
1620
+ end
1621
+ end
1622
+ if @continuations.num_waiting > 0
1623
+ @threads_waiting_on_continuations.each do |t|
1624
+ t.run
1625
+ end
1626
+ end
1627
+ if @basic_get_continuations.num_waiting > 0
1628
+ @threads_waiting_on_basic_get_continuations.each do |t|
1629
+ t.run
1630
+ end
1631
+ end
1632
+
1633
+ @continuations = ::Queue.new
1634
+ @confirms_continuations = ::Queue.new
1635
+ @basic_get_continuations = ::Queue.new
1636
+ end
1637
+
771
1638
  # Starts consumer work pool. Lazily called by #basic_consume to avoid creating new threads
772
1639
  # that won't do any real work for channels that do not register consumers (e.g. only used for
773
1640
  # publishing). MK.
1641
+ # @private
774
1642
  def maybe_start_consumer_work_pool!
775
1643
  @work_pool.start unless @work_pool.started?
776
1644
  end
777
1645
 
1646
+ # @private
778
1647
  def maybe_pause_consumer_work_pool!
779
1648
  @work_pool.pause if @work_pool && @work_pool.started?
780
1649
  end
781
1650
 
1651
+ # @private
782
1652
  def maybe_kill_consumer_work_pool!
783
1653
  @work_pool.kill if @work_pool && @work_pool.started?
784
1654
  end
785
1655
 
1656
+ # @private
786
1657
  def read_next_frame(options = {})
787
1658
  @connection.read_next_frame(options = {})
788
1659
  end
789
1660
 
790
- # Synchronizes given block using this channel's mutex.
791
- # @api public
792
- def synchronize(&block)
793
- @publishing_mutex.synchronize(&block)
794
- end
795
-
1661
+ # @private
796
1662
  def deregister_queue(queue)
797
1663
  @queues.delete(queue.name)
798
1664
  end
799
1665
 
1666
+ # @private
800
1667
  def deregister_queue_named(name)
801
1668
  @queues.delete(name)
802
1669
  end
803
1670
 
1671
+ # @private
804
1672
  def register_queue(queue)
805
1673
  @queues[queue.name] = queue
806
1674
  end
807
1675
 
1676
+ # @private
808
1677
  def find_queue(name)
809
1678
  @queues[name]
810
1679
  end
811
1680
 
1681
+ # @private
812
1682
  def deregister_exchange(exchange)
813
1683
  @exchanges.delete(exchange.name)
814
1684
  end
815
1685
 
1686
+ # @private
816
1687
  def register_exchange(exchange)
817
1688
  @exchanges[exchange.name] = exchange
818
1689
  end
819
1690
 
1691
+ # @private
820
1692
  def find_exchange(name)
821
1693
  @exchanges[name]
822
1694
  end
823
1695
 
824
- # Unique string supposed to be used as a consumer tag.
825
- #
826
- # @return [String] Unique string.
827
- # @api plugin
828
- def generate_consumer_tag(name = "bunny")
829
- "#{name}-#{Time.now.to_i * 1000}-#{Kernel.rand(999_999_999_999)}"
830
- end
831
-
832
1696
  protected
833
1697
 
1698
+ # @private
834
1699
  def closed!
835
1700
  @status = :closed
836
1701
  @work_pool.shutdown
837
1702
  @connection.release_channel_id(@id)
838
1703
  end
839
1704
 
1705
+ # @private
840
1706
  def instantiate_channel_level_exception(frame)
841
1707
  case frame
842
1708
  when AMQ::Protocol::Channel::Close then
@@ -857,10 +1723,12 @@ module Bunny
857
1723
  end
858
1724
  end
859
1725
 
1726
+ # @private
860
1727
  def raise_if_continuation_resulted_in_a_channel_error!
861
1728
  raise @last_channel_error if @last_channel_error
862
1729
  end
863
1730
 
1731
+ # @private
864
1732
  def raise_if_no_longer_open!
865
1733
  raise ChannelAlreadyClosed.new("cannot use a channel that was already closed! Channel id: #{@id}", self) if closed?
866
1734
  end