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 +1 -0
- data/ChangeLog.md +56 -0
- data/Gemfile +5 -0
- data/README.md +12 -6
- data/bunny.gemspec +1 -1
- data/examples/connection/automatic_recovery_with_basic_get.rb +34 -0
- data/examples/connection/automatic_recovery_with_client_named_queues.rb +6 -3
- data/examples/connection/automatic_recovery_with_server_named_queues.rb +6 -3
- data/lib/bunny/channel.rb +958 -90
- data/lib/bunny/channel_id_allocator.rb +10 -1
- data/lib/bunny/consumer.rb +44 -10
- data/lib/bunny/exceptions.rb +9 -0
- data/lib/bunny/exchange.rb +91 -12
- data/lib/bunny/main_loop.rb +27 -23
- data/lib/bunny/queue.rb +129 -9
- data/lib/bunny/session.rb +118 -24
- data/lib/bunny/system_timer.rb +5 -1
- data/lib/bunny/transport.rb +8 -5
- data/lib/bunny/version.rb +1 -1
- data/spec/higher_level_api/integration/basic_ack_spec.rb +5 -3
- data/spec/higher_level_api/integration/basic_consume_spec.rb +30 -2
- data/spec/higher_level_api/integration/basic_nack_spec.rb +22 -0
- data/spec/higher_level_api/integration/basic_publish_spec.rb +0 -47
- data/spec/higher_level_api/integration/basic_reject_spec.rb +22 -0
- data/spec/higher_level_api/integration/predeclared_exchanges_spec.rb +24 -0
- data/spec/higher_level_api/integration/publishing_edge_cases_spec.rb +63 -0
- data/spec/issues/issue97_attachment.json +1 -0
- data/spec/issues/issue97_spec.rb +174 -0
- metadata +214 -197
data/.gitignore
CHANGED
data/ChangeLog.md
CHANGED
@@ -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
data/README.md
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
#
|
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
|
-
##
|
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://
|
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
|
data/bunny.gemspec
CHANGED
@@ -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
|
26
|
-
|
27
|
-
|
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
|
26
|
-
|
27
|
-
|
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
|
data/lib/bunny/channel.rb
CHANGED
@@ -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
|
21
|
-
|
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
|
43
|
-
@confirms_continuations
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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(
|
214
|
-
@last_basic_qos_ok =
|
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(
|
228
|
-
@last_basic_recover_ok =
|
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(
|
284
|
-
@last_basic_consume_ok =
|
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(
|
312
|
-
@last_basic_consume_ok =
|
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(
|
326
|
-
@last_basic_cancel_ok =
|
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 =
|
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(
|
362
|
-
@last_queue_delete_ok =
|
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(
|
375
|
-
@last_queue_purge_ok =
|
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(
|
398
|
-
@last_queue_bind_ok =
|
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(
|
420
|
-
@last_queue_unbind_ok =
|
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(
|
443
|
-
@last_exchange_declare_ok =
|
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(
|
458
|
-
@last_exchange_delete_ok =
|
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(
|
487
|
-
@last_exchange_bind_ok =
|
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(
|
516
|
-
@last_exchange_unbind_ok =
|
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
|
-
#
|
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(
|
530
|
-
@last_channel_flow_ok =
|
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
|
-
#
|
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(
|
544
|
-
@last_tx_select_ok =
|
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(
|
556
|
-
@last_tx_commit_ok =
|
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(
|
568
|
-
@last_tx_rollback_ok =
|
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
|
-
#
|
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(
|
590
|
-
@last_confirm_select_ok =
|
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
|
-
|
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}
|
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
|
-
|
717
|
-
|
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
|
-
@
|
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
|
-
@
|
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
|
-
#
|
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
|