bunny 0.9.0.pre6 → 0.9.0.pre7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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