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 +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
@@ -8,6 +8,7 @@ module Bunny
|
|
8
8
|
# API
|
9
9
|
#
|
10
10
|
|
11
|
+
# @param [Integer] max_channel Max allowed channel id
|
11
12
|
def initialize(max_channel = ((1 << 16) - 1))
|
12
13
|
@int_allocator ||= AMQ::IntAllocator.new(1, max_channel)
|
13
14
|
|
@@ -29,7 +30,7 @@ module Bunny
|
|
29
30
|
|
30
31
|
# Releases previously allocated channel id. This method is thread safe.
|
31
32
|
#
|
32
|
-
# @param [Fixnum] Channel id to release
|
33
|
+
# @param [Fixnum] i Channel id to release
|
33
34
|
# @api public
|
34
35
|
# @see ChannelManager#next_channel_id
|
35
36
|
# @see ChannelManager#reset_channel_id_allocator
|
@@ -40,6 +41,14 @@ module Bunny
|
|
40
41
|
end # self.release_channel_id(i)
|
41
42
|
|
42
43
|
|
44
|
+
# Returns true if given channel id has been previously allocated and not yet released.
|
45
|
+
# This method is thread safe.
|
46
|
+
#
|
47
|
+
# @param [Fixnum] i Channel id to check
|
48
|
+
# @return [Boolean] true if given channel id has been previously allocated and not yet released
|
49
|
+
# @api public
|
50
|
+
# @see ChannelManager#next_channel_id
|
51
|
+
# @see ChannelManager#release_channel_id
|
43
52
|
def allocated_channel_id?(i)
|
44
53
|
@channel_id_mutex.synchronize do
|
45
54
|
@int_allocator.allocated?(i)
|
data/lib/bunny/consumer.rb
CHANGED
@@ -1,4 +1,13 @@
|
|
1
1
|
module Bunny
|
2
|
+
# Base class that represents consumer interface. Subclasses of this class implement
|
3
|
+
# specific logic of handling consumer life cycle events. Note that when the only event
|
4
|
+
# you are interested in is message deliveries, it is recommended to just use
|
5
|
+
# {Bunny::Queue#subscribe} instead of subclassing this class.
|
6
|
+
#
|
7
|
+
# @see Bunny::Queue#subscribe
|
8
|
+
# @see Bunny::Queue#subscribe_with
|
9
|
+
# @see http://rubybunny.info/articles/queues.html Queues and Consumers guide
|
10
|
+
# @api public
|
2
11
|
class Consumer
|
3
12
|
|
4
13
|
#
|
@@ -13,7 +22,15 @@ module Bunny
|
|
13
22
|
attr_reader :exclusive
|
14
23
|
|
15
24
|
|
16
|
-
|
25
|
+
# @param [Bunny::Channel] channel Channel this consumer will use
|
26
|
+
# @param [Bunny::Queue,String] queue Queue messages will be consumed from
|
27
|
+
# @param [String] consumer_tag Consumer tag (unique identifier). Generally it is better to let Bunny generate one.
|
28
|
+
# Empty string means RabbitMQ will generate consumer tag.
|
29
|
+
# @param [Boolean] no_ack (false) If false, delivered messages will be automatically acknowledged.
|
30
|
+
# If true, manual acknowledgements will be necessary.
|
31
|
+
# @param [Boolean] exclusive (false) Should this consumer be exclusive?
|
32
|
+
# @param [Hash] arguments (nil) Optional arguments that may be used by RabbitMQ extensions, etc
|
33
|
+
# @api public
|
17
34
|
def initialize(channel, queue, consumer_tag = channel.generate_consumer_tag, no_ack = true, exclusive = false, arguments = {})
|
18
35
|
@channel = channel || raise(ArgumentError, "channel is nil")
|
19
36
|
@queue = queue || raise(ArgumentError, "queue is nil")
|
@@ -23,34 +40,41 @@ module Bunny
|
|
23
40
|
@no_ack = no_ack
|
24
41
|
end
|
25
42
|
|
26
|
-
|
43
|
+
# Defines message delivery handler
|
44
|
+
# @api public
|
27
45
|
def on_delivery(&block)
|
28
46
|
@on_delivery = block
|
29
47
|
self
|
30
48
|
end
|
31
49
|
|
50
|
+
# Invokes message delivery handler
|
51
|
+
# @private
|
32
52
|
def call(*args)
|
33
53
|
@on_delivery.call(*args) if @on_delivery
|
34
54
|
end
|
35
55
|
alias handle_delivery call
|
36
56
|
|
57
|
+
# Defines consumer cancellation notification handler
|
58
|
+
#
|
59
|
+
# @see http://rubybunny.info/articles/queues.html Queues and Consumers guide
|
60
|
+
# @see http://rubybunny.info/articles/extensions.html RabbitMQ Extensions guide
|
61
|
+
# @api public
|
37
62
|
def on_cancellation(&block)
|
38
63
|
@on_cancellation = block
|
39
64
|
self
|
40
65
|
end
|
41
66
|
|
67
|
+
# Invokes consumer cancellation notification handler
|
68
|
+
# @private
|
42
69
|
def handle_cancellation(basic_cancel)
|
43
70
|
@on_cancellation.call(basic_cancel) if @on_cancellation
|
44
71
|
end
|
45
72
|
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
end
|
52
|
-
end
|
53
|
-
|
73
|
+
# Cancels this consumer. Messages for this consumer will no longer be delivered. If the queue
|
74
|
+
# it was on is auto-deleted and this consumer was the last one, the queue will be deleted.
|
75
|
+
#
|
76
|
+
# @see http://rubybunny.info/articles/queues.html Queues and Consumers guide
|
77
|
+
# @api public
|
54
78
|
def cancel
|
55
79
|
@channel.basic_cancel(@consumer_tag)
|
56
80
|
end
|
@@ -63,8 +87,18 @@ module Bunny
|
|
63
87
|
# Recovery
|
64
88
|
#
|
65
89
|
|
90
|
+
# @private
|
66
91
|
def recover_from_network_failure
|
67
92
|
@channel.basic_consume_with(self)
|
68
93
|
end
|
94
|
+
|
95
|
+
# @private
|
96
|
+
def queue_name
|
97
|
+
if @queue.respond_to?(:name)
|
98
|
+
@queue.name
|
99
|
+
else
|
100
|
+
@queue
|
101
|
+
end
|
102
|
+
end
|
69
103
|
end
|
70
104
|
end
|
data/lib/bunny/exceptions.rb
CHANGED
@@ -136,4 +136,13 @@ module Bunny
|
|
136
136
|
|
137
137
|
class UnexpectedFrame < ConnectionLevelException
|
138
138
|
end
|
139
|
+
|
140
|
+
class NetworkErrorWrapper < StandardError
|
141
|
+
attr_reader :other
|
142
|
+
|
143
|
+
def initialize(other)
|
144
|
+
super(other.message)
|
145
|
+
@other = other
|
146
|
+
end
|
147
|
+
end
|
139
148
|
end
|
data/lib/bunny/exchange.rb
CHANGED
@@ -1,6 +1,10 @@
|
|
1
1
|
require "bunny/compatibility"
|
2
2
|
|
3
3
|
module Bunny
|
4
|
+
# Represents AMQP 0.9.1 exchanges.
|
5
|
+
#
|
6
|
+
# @see http://rubybunny.info/articles/exchanges.html Exchanges and Publishing guide
|
7
|
+
# @see http://rubybunny.info/articles/extensions.html RabbitMQ Extensions guide
|
4
8
|
class Exchange
|
5
9
|
|
6
10
|
include Bunny::Compatibility
|
@@ -36,15 +40,16 @@ module Bunny
|
|
36
40
|
# a routing key of "weather.usa.ca.sandiego" and there is a queue Q with this name,
|
37
41
|
# that message will be routed to Q.
|
38
42
|
#
|
39
|
-
# @param [Bunny::Channel]
|
43
|
+
# @param [Bunny::Channel] channel_or_connection Channel to use. {Bunny::Session} instances
|
44
|
+
# are only supported for backwards compatibility.
|
40
45
|
#
|
41
46
|
# @example Publishing a messages to the tasks queue
|
42
47
|
# channel = Bunny::Channel.new(connection)
|
43
48
|
# tasks_queue = channel.queue("tasks")
|
44
49
|
# Bunny::Exchange.default(channel).publish("make clean", routing_key => "tasks")
|
45
50
|
#
|
46
|
-
# @see
|
47
|
-
# @see http://
|
51
|
+
# @see http://rubybunny.info/articles/exchanges.html Exchanges and Publishing guide
|
52
|
+
# @see http://www.rabbitmq.com/resources/specs/amqp0-9-1.pdf AMQP 0.9.1 specification (Section 2.1.2.4)
|
48
53
|
# @note Do not confuse default exchange with amq.direct: amq.direct is a pre-defined direct
|
49
54
|
# exchange that doesn't have any special routing semantics.
|
50
55
|
# @return [Exchange] An instance that corresponds to the default exchange (of type direct).
|
@@ -53,7 +58,23 @@ module Bunny
|
|
53
58
|
self.new(channel_from(channel_or_connection), :direct, AMQ::Protocol::EMPTY_STRING, :no_declare => true)
|
54
59
|
end
|
55
60
|
|
56
|
-
|
61
|
+
# @param [Bunny::Channel] channel_or_connection Channel this exchange will use. {Bunny::Session} instances are supported only for
|
62
|
+
# backwards compatibility with 0.8.
|
63
|
+
# @param [Symbol,String] type Exchange type
|
64
|
+
# @param [String] name Exchange name
|
65
|
+
# @param [Hash] opts Exchange properties
|
66
|
+
#
|
67
|
+
# @option opts [Boolean] :durable (false) Should this exchange be durable?
|
68
|
+
# @option opts [Boolean] :auto_delete (false) Should this exchange be automatically deleted when it is no longer used?
|
69
|
+
# @option opts [Boolean] :arguments ({}) Additional optional arguments (typically used by RabbitMQ extensions and plugins)
|
70
|
+
#
|
71
|
+
# @see Bunny::Channel#topic
|
72
|
+
# @see Bunny::Channel#fanout
|
73
|
+
# @see Bunny::Channel#direct
|
74
|
+
# @see Bunny::Channel#headers
|
75
|
+
# @see http://rubybunny.info/articles/exchanges.html Exchanges and Publishing guide
|
76
|
+
# @see http://rubybunny.info/articles/extensions.html RabbitMQ Extensions guide
|
77
|
+
# @api public
|
57
78
|
def initialize(channel_or_connection, type, name, opts = {})
|
58
79
|
# old Bunny versions pass a connection here. In that case,
|
59
80
|
# we just use default channel from it. MK.
|
@@ -66,7 +87,7 @@ module Bunny
|
|
66
87
|
@auto_delete = @options[:auto_delete]
|
67
88
|
@arguments = @options[:arguments]
|
68
89
|
|
69
|
-
declare! unless opts[:no_declare] ||
|
90
|
+
declare! unless opts[:no_declare] || predeclared? || (@name == AMQ::Protocol::EMPTY_STRING)
|
70
91
|
|
71
92
|
@channel.register_exchange(self)
|
72
93
|
end
|
@@ -83,15 +104,36 @@ module Bunny
|
|
83
104
|
@auto_delete
|
84
105
|
end # auto_delete?
|
85
106
|
|
107
|
+
# @return [Hash] Additional optional arguments (typically used by RabbitMQ extensions and plugins)
|
108
|
+
# @api public
|
86
109
|
def arguments
|
87
110
|
@arguments
|
88
111
|
end
|
89
112
|
|
90
|
-
def predeclared?
|
91
|
-
@name == AMQ::Protocol::EMPTY_STRING || (@name =~ /^amq\.(direct|fanout|topic|match|headers)/)
|
92
|
-
end
|
93
|
-
|
94
113
|
|
114
|
+
# Publishes a message
|
115
|
+
#
|
116
|
+
# @param [String] payload Message payload. It will never be modified by Bunny or RabbitMQ in any way.
|
117
|
+
# @param [Hash] opts Message properties (metadata) and delivery settings
|
118
|
+
#
|
119
|
+
# @option opts [String] :routing_key Routing key
|
120
|
+
# @option opts [Boolean] :persistent Should the message be persisted to disk?
|
121
|
+
# @option opts [Boolean] :mandatory Should the message be returned if it cannot be routed to any queue?
|
122
|
+
# @option opts [Integer] :timestamp A timestamp associated with this message
|
123
|
+
# @option opts [Integer] :expiration Expiration time after which the message will be deleted
|
124
|
+
# @option opts [String] :type Message type, e.g. what type of event or command this message represents. Can be any string
|
125
|
+
# @option opts [String] :reply_to Queue name other apps should send the response to
|
126
|
+
# @option opts [String] :content_type Message content type (e.g. application/json)
|
127
|
+
# @option opts [String] :content_encoding Message content encoding (e.g. gzip)
|
128
|
+
# @option opts [String] :correlation_id Message correlated to this one, e.g. what request this message is a reply for
|
129
|
+
# @option opts [Integer] :priority Message priority, 0 to 9. Not used by RabbitMQ, only applications
|
130
|
+
# @option opts [String] :message_id Any message identifier
|
131
|
+
# @option opts [String] :user_id Optional user ID. Verified by RabbitMQ against the actual connection username
|
132
|
+
# @option opts [String] :app_id Optional application ID
|
133
|
+
#
|
134
|
+
# @return [Bunny::Exchange] Self
|
135
|
+
# @see http://rubybunny.info/articles/exchanges.html Exchanges and Publishing guide
|
136
|
+
# @api public
|
95
137
|
def publish(payload, opts = {})
|
96
138
|
@channel.basic_publish(payload, self.name, (opts.delete(:routing_key) || opts.delete(:key)), opts)
|
97
139
|
|
@@ -100,32 +142,67 @@ module Bunny
|
|
100
142
|
|
101
143
|
|
102
144
|
# Deletes the exchange unless it is a default exchange
|
145
|
+
#
|
146
|
+
# @param [Hash] opts Options
|
147
|
+
#
|
148
|
+
# @option opts [Boolean] if_unused (false) Should this exchange be deleted only if it is no longer used
|
149
|
+
#
|
150
|
+
# @see http://rubybunny.info/articles/exchanges.html Exchanges and Publishing guide
|
103
151
|
# @api public
|
104
152
|
def delete(opts = {})
|
105
153
|
@channel.deregister_exchange(self)
|
106
154
|
@channel.exchange_delete(@name, opts) unless predeclared?
|
107
155
|
end
|
108
156
|
|
109
|
-
|
157
|
+
# Binds an exchange to another (source) exchange using exchange.bind AMQP 0.9.1 extension
|
158
|
+
# that RabbitMQ provides.
|
159
|
+
#
|
160
|
+
# @param [String] source Source exchange name
|
161
|
+
# @param [Hash] opts Options
|
162
|
+
#
|
163
|
+
# @option opts [String] routing_key (nil) Routing key used for binding
|
164
|
+
# @option opts [Hash] arguments ({}) Optional arguments
|
165
|
+
#
|
166
|
+
# @return [Bunny::Exchange] Self
|
167
|
+
# @see http://rubybunny.info/articles/exchanges.html Exchanges and Publishing guide
|
168
|
+
# @see http://rubybunny.info/articles/bindings.html Bindings guide
|
169
|
+
# @see http://rubybunny.info/articles/extensions.html RabbitMQ Extensions guide
|
170
|
+
# @api public
|
110
171
|
def bind(source, opts = {})
|
111
172
|
@channel.exchange_bind(source, self, opts)
|
112
173
|
|
113
174
|
self
|
114
175
|
end
|
115
176
|
|
177
|
+
# Unbinds an exchange from another (source) exchange using exchange.unbind AMQP 0.9.1 extension
|
178
|
+
# that RabbitMQ provides.
|
179
|
+
#
|
180
|
+
# @param [String] source Source exchange name
|
181
|
+
# @param [Hash] opts Options
|
182
|
+
#
|
183
|
+
# @option opts [String] routing_key (nil) Routing key used for binding
|
184
|
+
# @option opts [Hash] arguments ({}) Optional arguments
|
185
|
+
#
|
186
|
+
# @return [Bunny::Exchange] Self
|
187
|
+
# @see http://rubybunny.info/articles/exchanges.html Exchanges and Publishing guide
|
188
|
+
# @see http://rubybunny.info/articles/bindings.html Bindings guide
|
189
|
+
# @see http://rubybunny.info/articles/extensions.html RabbitMQ Extensions guide
|
190
|
+
# @api public
|
116
191
|
def unbind(source, opts = {})
|
117
192
|
@channel.exchange_unbind(source, self, opts)
|
118
193
|
|
119
194
|
self
|
120
195
|
end
|
121
196
|
|
122
|
-
|
197
|
+
# Defines a block that will handle returned messages
|
198
|
+
# @see http://rubybunny.info/articles/exchanges.html
|
123
199
|
def on_return(&block)
|
124
200
|
@on_return = block
|
125
201
|
|
126
202
|
self
|
127
203
|
end
|
128
204
|
|
205
|
+
# @private
|
129
206
|
def recover_from_network_failure
|
130
207
|
# puts "Recovering exchange #{@name} from network failure"
|
131
208
|
declare! unless predefined?
|
@@ -136,6 +213,7 @@ module Bunny
|
|
136
213
|
# Implementation
|
137
214
|
#
|
138
215
|
|
216
|
+
# @private
|
139
217
|
def handle_return(basic_return, properties, content)
|
140
218
|
if @on_return
|
141
219
|
@on_return.call(basic_return, properties, content)
|
@@ -146,8 +224,9 @@ module Bunny
|
|
146
224
|
|
147
225
|
# @return [Boolean] true if this exchange is a pre-defined one (amq.direct, amq.fanout, amq.match and so on)
|
148
226
|
def predefined?
|
149
|
-
|
227
|
+
(@name == AMQ::Protocol::EMPTY_STRING) || !!(@name =~ /^amq\.(direct|fanout|topic|headers|match)/i)
|
150
228
|
end # predefined?
|
229
|
+
alias predeclared? predefined?
|
151
230
|
|
152
231
|
protected
|
153
232
|
|
data/lib/bunny/main_loop.rb
CHANGED
@@ -16,6 +16,7 @@ module Bunny
|
|
16
16
|
|
17
17
|
def start
|
18
18
|
@thread = Thread.new(&method(:run_loop))
|
19
|
+
@thread.abort_on_exception = true
|
19
20
|
end
|
20
21
|
|
21
22
|
def resume
|
@@ -27,29 +28,7 @@ module Bunny
|
|
27
28
|
loop do
|
28
29
|
begin
|
29
30
|
break if @stopping || @network_is_down
|
30
|
-
|
31
|
-
frame = @transport.read_next_frame
|
32
|
-
@session.signal_activity!
|
33
|
-
|
34
|
-
next if frame.is_a?(AMQ::Protocol::HeartbeatFrame)
|
35
|
-
|
36
|
-
if !frame.final? || frame.method_class.has_content?
|
37
|
-
header = @transport.read_next_frame
|
38
|
-
content = ''
|
39
|
-
|
40
|
-
if header.body_size > 0
|
41
|
-
loop do
|
42
|
-
body_frame = @transport.read_next_frame
|
43
|
-
content << body_frame.decode_payload
|
44
|
-
|
45
|
-
break if content.bytesize >= header.body_size
|
46
|
-
end
|
47
|
-
end
|
48
|
-
|
49
|
-
@session.handle_frameset(frame.channel, [frame.decode_payload, header.decode_payload, content])
|
50
|
-
else
|
51
|
-
@session.handle_frame(frame.channel, frame.decode_payload)
|
52
|
-
end
|
31
|
+
run_once
|
53
32
|
rescue Timeout::Error => te
|
54
33
|
# given that the server may be pushing data to us, timeout detection/handling
|
55
34
|
# should happen per operation and not in this loop
|
@@ -67,6 +46,31 @@ module Bunny
|
|
67
46
|
end
|
68
47
|
end
|
69
48
|
|
49
|
+
def run_once
|
50
|
+
frame = @transport.read_next_frame
|
51
|
+
@session.signal_activity!
|
52
|
+
|
53
|
+
return if frame.is_a?(AMQ::Protocol::HeartbeatFrame)
|
54
|
+
|
55
|
+
if !frame.final? || frame.method_class.has_content?
|
56
|
+
header = @transport.read_next_frame
|
57
|
+
content = ''
|
58
|
+
|
59
|
+
if header.body_size > 0
|
60
|
+
loop do
|
61
|
+
body_frame = @transport.read_next_frame
|
62
|
+
content << body_frame.decode_payload
|
63
|
+
|
64
|
+
break if content.bytesize >= header.body_size
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
@session.handle_frameset(frame.channel, [frame.decode_payload, header.decode_payload, content])
|
69
|
+
else
|
70
|
+
@session.handle_frame(frame.channel, frame.decode_payload)
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
70
74
|
def stop
|
71
75
|
@stopping = true
|
72
76
|
end
|
data/lib/bunny/queue.rb
CHANGED
@@ -1,6 +1,10 @@
|
|
1
1
|
require "bunny/compatibility"
|
2
2
|
|
3
3
|
module Bunny
|
4
|
+
# Represents AMQP 0.9.1 queue.
|
5
|
+
#
|
6
|
+
# @see http://rubybunny.info/articles/queues.html Queues and Consumers guide
|
7
|
+
# @see http://rubybunny.info/articles/extensions.html RabbitMQ Extensions guide
|
4
8
|
class Queue
|
5
9
|
|
6
10
|
include Bunny::Compatibility
|
@@ -10,8 +14,27 @@ module Bunny
|
|
10
14
|
# API
|
11
15
|
#
|
12
16
|
|
13
|
-
|
14
|
-
|
17
|
+
# @return [Bunny::Channel] Channel this queue uses
|
18
|
+
attr_reader :channel
|
19
|
+
# @return [String] Queue name
|
20
|
+
attr_reader :name
|
21
|
+
# @return [Hash] Options this queue was created with
|
22
|
+
attr_reader :options
|
23
|
+
|
24
|
+
# @param [Bunny::Channel] channel_or_connection Channel this queue will use. {Bunny::Session} instances are supported only for
|
25
|
+
# backwards compatibility with 0.8.
|
26
|
+
# @param [String] name Queue name. Pass an empty string to make RabbitMQ generate a unique one.
|
27
|
+
# @param [Hash] opts Queue properties
|
28
|
+
#
|
29
|
+
# @option opts [Boolean] :durable (false) Should this queue be durable?
|
30
|
+
# @option opts [Boolean] :auto_delete (false) Should this queue be automatically deleted when the last consumer disconnects?
|
31
|
+
# @option opts [Boolean] :exclusive (false) Should this queue be exclusive (only can be used by this connection, removed when the connection is closed)?
|
32
|
+
# @option opts [Boolean] :arguments ({}) Additional optional arguments (typically used by RabbitMQ extensions and plugins)
|
33
|
+
#
|
34
|
+
# @see Bunny::Channel#queue
|
35
|
+
# @see http://rubybunny.info/articles/queues.html Queues and Consumers guide
|
36
|
+
# @see http://rubybunny.info/articles/extensions.html RabbitMQ Extensions guide
|
37
|
+
# @api public
|
15
38
|
def initialize(channel_or_connection, name = AMQ::Protocol::EMPTY_STRING, opts = {})
|
16
39
|
# old Bunny versions pass a connection here. In that case,
|
17
40
|
# we just use default channel from it. MK.
|
@@ -37,33 +60,50 @@ module Bunny
|
|
37
60
|
|
38
61
|
# @return [Boolean] true if this queue was declared as durable (will survive broker restart).
|
39
62
|
# @api public
|
63
|
+
# @see http://rubybunny.info/articles/queues.html Queues and Consumers guide
|
40
64
|
def durable?
|
41
65
|
@durable
|
42
66
|
end # durable?
|
43
67
|
|
44
68
|
# @return [Boolean] true if this queue was declared as exclusive (limited to just one consumer)
|
45
69
|
# @api public
|
70
|
+
# @see http://rubybunny.info/articles/queues.html Queues and Consumers guide
|
46
71
|
def exclusive?
|
47
72
|
@exclusive
|
48
73
|
end # exclusive?
|
49
74
|
|
50
75
|
# @return [Boolean] true if this queue was declared as automatically deleted (deleted as soon as last consumer unbinds).
|
51
76
|
# @api public
|
77
|
+
# @see http://rubybunny.info/articles/queues.html Queues and Consumers guide
|
52
78
|
def auto_delete?
|
53
79
|
@auto_delete
|
54
80
|
end # auto_delete?
|
55
81
|
|
56
82
|
# @return [Boolean] true if this queue was declared as server named.
|
57
83
|
# @api public
|
84
|
+
# @see http://rubybunny.info/articles/queues.html Queues and Consumers guide
|
58
85
|
def server_named?
|
59
86
|
@server_named
|
60
87
|
end # server_named?
|
61
88
|
|
89
|
+
# @return [Hash] Additional optional arguments (typically used by RabbitMQ extensions and plugins)
|
90
|
+
# @api public
|
62
91
|
def arguments
|
63
92
|
@arguments
|
64
93
|
end
|
65
94
|
|
66
95
|
|
96
|
+
# Binds queue to an exchange
|
97
|
+
#
|
98
|
+
# @param [Bunny::Exchange,String] exchange Exchange to bind to
|
99
|
+
# @param [Hash] opts Binding properties
|
100
|
+
#
|
101
|
+
# @option opts [String] :routing_key Routing key
|
102
|
+
# @option opts [Hash] :arguments ({}) Additional optional binding arguments
|
103
|
+
#
|
104
|
+
# @see http://rubybunny.info/articles/queues.html Queues and Consumers guide
|
105
|
+
# @see http://rubybunny.info/articles/bindings.html Bindings guide
|
106
|
+
# @api public
|
67
107
|
def bind(exchange, opts = {})
|
68
108
|
@channel.queue_bind(@name, exchange, opts)
|
69
109
|
|
@@ -82,6 +122,17 @@ module Bunny
|
|
82
122
|
self
|
83
123
|
end
|
84
124
|
|
125
|
+
# Unbinds queue from an exchange
|
126
|
+
#
|
127
|
+
# @param [Bunny::Exchange,String] exchange Exchange to unbind from
|
128
|
+
# @param [Hash] opts Binding properties
|
129
|
+
#
|
130
|
+
# @option opts [String] :routing_key Routing key
|
131
|
+
# @option opts [Hash] :arguments ({}) Additional optional binding arguments
|
132
|
+
#
|
133
|
+
# @see http://rubybunny.info/articles/queues.html Queues and Consumers guide
|
134
|
+
# @see http://rubybunny.info/articles/bindings.html Bindings guide
|
135
|
+
# @api public
|
85
136
|
def unbind(exchange, opts = {})
|
86
137
|
@channel.queue_unbind(@name, exchange, opts)
|
87
138
|
|
@@ -97,6 +148,19 @@ module Bunny
|
|
97
148
|
self
|
98
149
|
end
|
99
150
|
|
151
|
+
# Adds a consumer to the queue (subscribes for message deliveries).
|
152
|
+
#
|
153
|
+
# @param [Hash] opts Options
|
154
|
+
#
|
155
|
+
# @option opts [Boolean] :manual_ack (false) Will this consumer use manual acknowledgements?
|
156
|
+
# @option opts [Boolean] :exclusive (false) Should this consumer be exclusive for this queue?
|
157
|
+
# @option opts [Boolean] :block (false) Should the call block calling thread?
|
158
|
+
# @option opts [#call] :on_cancellation Block to execute when this consumer is cancelled remotely (e.g. via the RabbitMQ Management plugin)
|
159
|
+
# @option opts [String] :consumer_tag Unique consumer identifier. It is usually recommended to let Bunny generate it for you.
|
160
|
+
# @option opts [Hash] :arguments ({}) Additional (optional) arguments, typically used by RabbitMQ extensions
|
161
|
+
#
|
162
|
+
# @see http://rubybunny.info/articles/queues.html Queues and Consumers guide
|
163
|
+
# @api public
|
100
164
|
def subscribe(opts = {
|
101
165
|
:consumer_tag => @channel.generate_consumer_tag,
|
102
166
|
:ack => false,
|
@@ -109,10 +173,10 @@ module Bunny
|
|
109
173
|
consumer = Consumer.new(@channel,
|
110
174
|
self,
|
111
175
|
ctag,
|
112
|
-
!opts[:ack],
|
176
|
+
!(opts[:ack] || opts[:manual_ack]),
|
113
177
|
opts[:exclusive],
|
114
178
|
opts[:arguments])
|
115
|
-
|
179
|
+
|
116
180
|
consumer.on_delivery(&block)
|
117
181
|
consumer.on_cancellation(&opts[:on_cancellation]) if opts[:on_cancellation]
|
118
182
|
|
@@ -126,6 +190,15 @@ module Bunny
|
|
126
190
|
consumer
|
127
191
|
end
|
128
192
|
|
193
|
+
# Adds a consumer object to the queue (subscribes for message deliveries).
|
194
|
+
#
|
195
|
+
# @param [Bunny::Consumer] consumer a {Bunny::Consumer} subclass that implements consumer interface
|
196
|
+
# @param [Hash] opts Options
|
197
|
+
#
|
198
|
+
# @option opts [Boolean] block (false) Should the call block calling thread?
|
199
|
+
#
|
200
|
+
# @see http://rubybunny.info/articles/queues.html Queues and Consumers guide
|
201
|
+
# @api public
|
129
202
|
def subscribe_with(consumer, opts = {:block => false})
|
130
203
|
@channel.basic_consume_with(consumer)
|
131
204
|
|
@@ -133,6 +206,29 @@ module Bunny
|
|
133
206
|
consumer
|
134
207
|
end
|
135
208
|
|
209
|
+
# @param [Hash] opts Options
|
210
|
+
#
|
211
|
+
# @option opts [Boolean] block (false) Should the call block calling thread?
|
212
|
+
#
|
213
|
+
# @return [Array] Triple of delivery info, message properties and message content.
|
214
|
+
# If the queue is empty, all three will be nils.
|
215
|
+
# @see http://rubybunny.info/articles/queues.html Queues and Consumers guide
|
216
|
+
# @see Bunny::Queue#subscribe
|
217
|
+
# @api public
|
218
|
+
#
|
219
|
+
# @example
|
220
|
+
# conn = Bunny.new
|
221
|
+
# conn.start
|
222
|
+
#
|
223
|
+
# ch = conn.create_channel
|
224
|
+
# q = ch.queue("test1")
|
225
|
+
# x = ch.default_exchange
|
226
|
+
# x.publish("Hello, everybody!", :routing_key => 'test1')
|
227
|
+
#
|
228
|
+
# delivery_info, properties, payload = q.pop
|
229
|
+
#
|
230
|
+
# puts "This is the message: " + payload + "\n\n"
|
231
|
+
# conn.close
|
136
232
|
def pop(opts = {:ack => false}, &block)
|
137
233
|
delivery_info, properties, content = @channel.basic_get(@name, opts)
|
138
234
|
|
@@ -144,6 +240,10 @@ module Bunny
|
|
144
240
|
end
|
145
241
|
alias get pop
|
146
242
|
|
243
|
+
# Version of {Bunny::Queue#pop} that returns data in legacy format
|
244
|
+
# (as a hash).
|
245
|
+
# @return [Hash]
|
246
|
+
# @deprecated
|
147
247
|
def pop_as_hash(opts = {:ack => false}, &block)
|
148
248
|
delivery_info, properties, content = @channel.basic_get(@name, opts)
|
149
249
|
|
@@ -156,10 +256,12 @@ module Bunny
|
|
156
256
|
end
|
157
257
|
end
|
158
258
|
|
159
|
-
# Publishes a message to the queue via default exchange.
|
259
|
+
# Publishes a message to the queue via default exchange. Takes the same arguments
|
260
|
+
# as {Bunny::Exchange#publish}
|
160
261
|
#
|
161
262
|
# @see Bunny::Exchange#publish
|
162
263
|
# @see Bunny::Channel#default_exchange
|
264
|
+
# @see http://rubybunny.info/articles/exchanges.html Exchanges and Publishing guide
|
163
265
|
def publish(payload, opts = {})
|
164
266
|
@channel.default_exchange.publish(payload, opts.merge(:routing_key => @name))
|
165
267
|
|
@@ -168,29 +270,44 @@ module Bunny
|
|
168
270
|
|
169
271
|
|
170
272
|
# Deletes the queue
|
273
|
+
#
|
274
|
+
# @param [Hash] opts Options
|
275
|
+
#
|
276
|
+
# @option opts [Boolean] if_unused (false) Should this queue be deleted only if it has no consumers?
|
277
|
+
# @option opts [Boolean] if_empty (false) Should this queue be deleted only if it has no messages?
|
278
|
+
#
|
279
|
+
# @see http://rubybunny.info/articles/queues.html Queues and Consumers guide
|
171
280
|
# @api public
|
172
281
|
def delete(opts = {})
|
173
282
|
@channel.deregister_queue(self)
|
174
283
|
@channel.queue_delete(@name, opts)
|
175
284
|
end
|
176
285
|
|
286
|
+
# Purges a queue (removes all messages from it)
|
287
|
+
# @see http://rubybunny.info/articles/queues.html Queues and Consumers guide
|
288
|
+
# @api public
|
177
289
|
def purge(opts = {})
|
178
290
|
@channel.queue_purge(@name, opts)
|
179
291
|
|
180
292
|
self
|
181
293
|
end
|
182
294
|
|
295
|
+
# @return [Hash] A hash with information about the number of queue messages and consumers
|
296
|
+
# @see #message_count
|
297
|
+
# @see #consumer_count
|
183
298
|
def status
|
184
299
|
queue_declare_ok = @channel.queue_declare(@name, @options.merge(:passive => true))
|
185
300
|
{:message_count => queue_declare_ok.message_count,
|
186
301
|
:consumer_count => queue_declare_ok.consumer_count}
|
187
302
|
end
|
188
303
|
|
304
|
+
# @return [Integer] How many messages the queue has ready (e.g. not delivered but not unacknowledged)
|
189
305
|
def message_count
|
190
306
|
s = self.status
|
191
307
|
s[:message_count]
|
192
308
|
end
|
193
309
|
|
310
|
+
# @return [Integer] How many active consumers the queue has
|
194
311
|
def consumer_count
|
195
312
|
s = self.status
|
196
313
|
s[:consumer_count]
|
@@ -200,9 +317,8 @@ module Bunny
|
|
200
317
|
# Recovery
|
201
318
|
#
|
202
319
|
|
320
|
+
# @private
|
203
321
|
def recover_from_network_failure
|
204
|
-
# puts "Recovering queue #{@name} from network failure"
|
205
|
-
|
206
322
|
if self.server_named?
|
207
323
|
old_name = @name.dup
|
208
324
|
@name = AMQ::Protocol::EMPTY_STRING
|
@@ -210,17 +326,21 @@ module Bunny
|
|
210
326
|
@channel.deregister_queue_named(old_name)
|
211
327
|
end
|
212
328
|
|
213
|
-
|
329
|
+
# puts "Recovering queue #{@name}"
|
214
330
|
begin
|
331
|
+
declare!
|
332
|
+
|
215
333
|
@channel.register_queue(self)
|
216
334
|
rescue Exception => e
|
217
|
-
puts "Caught #{e.inspect} while registering #{@name}!"
|
335
|
+
puts "Caught #{e.inspect} while redeclaring and registering #{@name}!"
|
218
336
|
end
|
219
337
|
recover_bindings
|
220
338
|
end
|
221
339
|
|
340
|
+
# @private
|
222
341
|
def recover_bindings
|
223
342
|
@bindings.each do |b|
|
343
|
+
# puts "Recovering binding #{b.inspect}"
|
224
344
|
self.bind(b[:exchange], b)
|
225
345
|
end
|
226
346
|
end
|