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
@@ -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
|