amqp 1.1.0.pre1 → 1.1.0.pre2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/ChangeLog.md +5 -0
- data/Gemfile +0 -2
- data/README.md +30 -24
- data/amqp.gemspec +0 -1
- data/lib/amq/protocol/get_response.rb +55 -0
- data/lib/amqp.rb +237 -3
- data/lib/amqp/auth_mechanism_adapter.rb +69 -0
- data/lib/amqp/auth_mechanism_adapter/external.rb +27 -0
- data/lib/amqp/auth_mechanism_adapter/plain.rb +24 -0
- data/lib/amqp/callbacks.rb +67 -0
- data/lib/amqp/channel.rb +517 -76
- data/lib/amqp/consumer.rb +200 -32
- data/lib/amqp/consumer_tag_generator.rb +20 -0
- data/lib/amqp/deferrable.rb +5 -0
- data/lib/amqp/entity.rb +71 -0
- data/lib/amqp/exchange.rb +266 -17
- data/lib/amqp/framing/string/frame.rb +36 -0
- data/lib/amqp/handlers_registry.rb +28 -0
- data/lib/amqp/openable.rb +58 -0
- data/lib/amqp/queue.rb +526 -19
- data/lib/amqp/session.rb +943 -70
- data/lib/amqp/settings.rb +157 -0
- data/lib/amqp/version.rb +1 -1
- data/spec/integration/authentication_spec.rb +1 -1
- data/spec/integration/extensions/rabbitmq/publisher_confirmations_spec.rb +0 -15
- data/spec/integration/store_and_forward_spec.rb +1 -1
- data/spec/spec_helper.rb +1 -1
- data/spec/unit/amqp/client_spec.rb +3 -1
- data/spec/unit/amqp/connection_spec.rb +10 -41
- metadata +15 -19
- data/lib/amqp/client.rb +0 -100
- data/lib/amqp/connection.rb +0 -223
@@ -0,0 +1,36 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require "amqp/exceptions"
|
4
|
+
|
5
|
+
module AMQP
|
6
|
+
module Framing
|
7
|
+
module String
|
8
|
+
class Frame < AMQ::Protocol::Frame
|
9
|
+
ENCODINGS_SUPPORTED = defined? Encoding
|
10
|
+
HEADER_SLICE = (0..6).freeze
|
11
|
+
DATA_SLICE = (7..-1).freeze
|
12
|
+
PAYLOAD_SLICE = (0..-2).freeze
|
13
|
+
|
14
|
+
def self.decode(string)
|
15
|
+
header = string[HEADER_SLICE]
|
16
|
+
type, channel, size = self.decode_header(header)
|
17
|
+
data = string[DATA_SLICE]
|
18
|
+
payload = data[PAYLOAD_SLICE]
|
19
|
+
frame_end = data[-1, 1]
|
20
|
+
|
21
|
+
frame_end.force_encoding(AMQ::Protocol::Frame::FINAL_OCTET.encoding) if ENCODINGS_SUPPORTED
|
22
|
+
|
23
|
+
# 1) the size is miscalculated
|
24
|
+
if payload.bytesize != size
|
25
|
+
raise BadLengthError.new(size, payload.bytesize)
|
26
|
+
end
|
27
|
+
|
28
|
+
# 2) the size is OK, but the string doesn't end with FINAL_OCTET
|
29
|
+
raise NoFinalOctetError.new if frame_end != AMQ::Protocol::Frame::FINAL_OCTET
|
30
|
+
|
31
|
+
self.new(type, payload, channel)
|
32
|
+
end # self.from
|
33
|
+
end # Frame
|
34
|
+
end # String
|
35
|
+
end # Framing
|
36
|
+
end # AMQP
|
@@ -0,0 +1,28 @@
|
|
1
|
+
module AMQP
|
2
|
+
class HandlersRegistry
|
3
|
+
|
4
|
+
@@handlers ||= Hash.new
|
5
|
+
|
6
|
+
|
7
|
+
#
|
8
|
+
# API
|
9
|
+
#
|
10
|
+
|
11
|
+
|
12
|
+
def self.register(klass, &block)
|
13
|
+
@@handlers[klass] = block
|
14
|
+
end
|
15
|
+
class << self
|
16
|
+
alias handle register
|
17
|
+
end
|
18
|
+
|
19
|
+
def self.find(klass)
|
20
|
+
@@handlers[klass]
|
21
|
+
end
|
22
|
+
|
23
|
+
def self.handlers
|
24
|
+
@@handlers
|
25
|
+
end
|
26
|
+
|
27
|
+
end # HandlersRegistry
|
28
|
+
end
|
@@ -0,0 +1,58 @@
|
|
1
|
+
module AMQP
|
2
|
+
module Openable
|
3
|
+
VALUES = [:opened, :closed, :opening, :closing].freeze
|
4
|
+
|
5
|
+
class ImproperStatusError < ArgumentError
|
6
|
+
def initialize(value)
|
7
|
+
super("Value #{value.inspect} isn't permitted. Choose one of: #{AMQP::Openable::VALUES.inspect}")
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
attr_reader :status
|
12
|
+
def status=(value)
|
13
|
+
if VALUES.include?(value)
|
14
|
+
@status = value
|
15
|
+
else
|
16
|
+
raise ImproperStatusError.new(value)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
def opened?
|
21
|
+
@status == :opened
|
22
|
+
end
|
23
|
+
alias open? opened?
|
24
|
+
|
25
|
+
def closed?
|
26
|
+
@status == :closed
|
27
|
+
end
|
28
|
+
|
29
|
+
|
30
|
+
|
31
|
+
def opening?
|
32
|
+
@status == :opening
|
33
|
+
end
|
34
|
+
|
35
|
+
def closing?
|
36
|
+
@status == :closing
|
37
|
+
end
|
38
|
+
|
39
|
+
|
40
|
+
def opened!
|
41
|
+
@status = :opened
|
42
|
+
end # opened!
|
43
|
+
|
44
|
+
def closed!
|
45
|
+
@status = :closed
|
46
|
+
end # closed!
|
47
|
+
|
48
|
+
|
49
|
+
|
50
|
+
def opening!
|
51
|
+
@status = :opening
|
52
|
+
end # opening!
|
53
|
+
|
54
|
+
def closing!
|
55
|
+
@status = :closing
|
56
|
+
end # closing!
|
57
|
+
end
|
58
|
+
end
|
data/lib/amqp/queue.rb
CHANGED
@@ -1,6 +1,8 @@
|
|
1
1
|
# encoding: utf-8
|
2
2
|
|
3
|
-
require "
|
3
|
+
require "amqp/entity"
|
4
|
+
|
5
|
+
require "amq/protocol/get_response"
|
4
6
|
require "amqp/consumer"
|
5
7
|
|
6
8
|
module AMQP
|
@@ -118,7 +120,17 @@ module AMQP
|
|
118
120
|
#
|
119
121
|
# @see http://files.travis-ci.org/docs/amqp/0.9.1/AMQP091Specification.pdf AMQP 0.9.1 specification (Section 2.1.1)
|
120
122
|
# @see AMQP::Exchange
|
121
|
-
class Queue
|
123
|
+
class Queue
|
124
|
+
|
125
|
+
#
|
126
|
+
# Behaviours
|
127
|
+
#
|
128
|
+
|
129
|
+
include Entity
|
130
|
+
include ServerNamedEntity
|
131
|
+
extend ProtocolMethodHandlers
|
132
|
+
|
133
|
+
|
122
134
|
|
123
135
|
#
|
124
136
|
# API
|
@@ -129,6 +141,22 @@ module AMQP
|
|
129
141
|
# Options this queue object was instantiated with
|
130
142
|
attr_accessor :opts
|
131
143
|
|
144
|
+
# Channel this queue belongs to.
|
145
|
+
# @return [AMQP::Channel]
|
146
|
+
attr_reader :channel
|
147
|
+
|
148
|
+
# @return [Array<Hash>] All consumers on this queue.
|
149
|
+
attr_reader :consumers
|
150
|
+
|
151
|
+
# @return [AMQP::Consumer] Default consumer (registered with {Queue#consume}).
|
152
|
+
attr_reader :default_consumer
|
153
|
+
|
154
|
+
# @return [Hash] Additional arguments given on queue declaration. Typically used by AMQP extensions.
|
155
|
+
attr_reader :arguments
|
156
|
+
|
157
|
+
# @return [Array<Hash>]
|
158
|
+
attr_reader :bindings
|
159
|
+
|
132
160
|
|
133
161
|
|
134
162
|
# @option opts [Boolean] :passive (false) If set, the server will not create the queue if it does not
|
@@ -167,7 +195,7 @@ module AMQP
|
|
167
195
|
#
|
168
196
|
# @yield [queue, declare_ok] Yields successfully declared queue instance and AMQP method (queue.declare-ok) instance. The latter is optional.
|
169
197
|
# @yieldparam [Queue] queue Queue that is successfully declared and is ready to be used.
|
170
|
-
# @yieldparam [
|
198
|
+
# @yieldparam [AMQ::Protocol::Queue::DeclareOk] declare_ok AMQP queue.declare-ok) instance.
|
171
199
|
#
|
172
200
|
# @api public
|
173
201
|
def initialize(channel, name = AMQ::Protocol::EMPTY_STRING, opts = {}, &block)
|
@@ -183,9 +211,26 @@ module AMQP
|
|
183
211
|
# a deferrable that we use to delay operations until this queue is actually declared.
|
184
212
|
# one reason for this is to support a case when a server-named queue is immediately bound.
|
185
213
|
# it's crazy, but 0.7.x supports it, so... MK.
|
186
|
-
@declaration_deferrable =
|
214
|
+
@declaration_deferrable = AMQP::Deferrable.new
|
215
|
+
|
216
|
+
super(channel.connection)
|
217
|
+
|
218
|
+
@name = name
|
219
|
+
# this has to stay true even after queue.declare-ok arrives. MK.
|
220
|
+
@server_named = @name.empty?
|
221
|
+
if @server_named
|
222
|
+
self.on_connection_interruption do
|
223
|
+
# server-named queue need to get new names after recovery. MK.
|
224
|
+
@name = AMQ::Protocol::EMPTY_STRING
|
225
|
+
end
|
226
|
+
end
|
187
227
|
|
188
|
-
|
228
|
+
@channel = channel
|
229
|
+
|
230
|
+
# primarily for autorecovery. MK.
|
231
|
+
@bindings = Array.new
|
232
|
+
|
233
|
+
@consumers = Hash.new
|
189
234
|
|
190
235
|
shim = Proc.new do |q, declare_ok|
|
191
236
|
case block.arity
|
@@ -202,11 +247,11 @@ module AMQP
|
|
202
247
|
end
|
203
248
|
|
204
249
|
if block
|
205
|
-
self.
|
250
|
+
self.queue_declare(@opts[:passive], @opts[:durable], @opts[:exclusive], @opts[:auto_delete], @opts[:nowait], @opts[:arguments], &shim)
|
206
251
|
else
|
207
|
-
# we cannot pass :nowait as true here,
|
252
|
+
# we cannot pass :nowait as true here, AMQP::Queue will (rightfully) raise an exception because
|
208
253
|
# it has no idea about crazy edge cases we are trying to support for sake of backwards compatibility. MK.
|
209
|
-
self.
|
254
|
+
self.queue_declare(@opts[:passive], @opts[:durable], @opts[:exclusive], @opts[:auto_delete], false, @opts[:arguments])
|
210
255
|
end
|
211
256
|
end
|
212
257
|
end
|
@@ -224,6 +269,25 @@ module AMQP
|
|
224
269
|
end # once_declared(&block)
|
225
270
|
|
226
271
|
|
272
|
+
# @return [Boolean] true if this queue was declared as durable (will survive broker restart).
|
273
|
+
# @api public
|
274
|
+
def durable?
|
275
|
+
@durable
|
276
|
+
end # durable?
|
277
|
+
|
278
|
+
# @return [Boolean] true if this queue was declared as exclusive (limited to just one consumer)
|
279
|
+
# @api public
|
280
|
+
def exclusive?
|
281
|
+
@exclusive
|
282
|
+
end # exclusive?
|
283
|
+
|
284
|
+
# @return [Boolean] true if this queue was declared as automatically deleted (deleted as soon as last consumer unbinds).
|
285
|
+
# @api public
|
286
|
+
def auto_delete?
|
287
|
+
@auto_delete
|
288
|
+
end # auto_delete?
|
289
|
+
|
290
|
+
|
227
291
|
# @return [Boolean] true if this queue is server-named
|
228
292
|
def server_named?
|
229
293
|
@server_named
|
@@ -282,7 +346,7 @@ module AMQP
|
|
282
346
|
def bind(exchange, opts = {}, &block)
|
283
347
|
@channel.once_open do
|
284
348
|
self.once_name_is_available do
|
285
|
-
|
349
|
+
queue_bind(exchange, (opts[:key] || opts[:routing_key] || AMQ::Protocol::EMPTY_STRING), (opts[:nowait] || block.nil?), opts[:arguments], &block)
|
286
350
|
end
|
287
351
|
end
|
288
352
|
|
@@ -351,7 +415,7 @@ module AMQP
|
|
351
415
|
def unbind(exchange, opts = {}, &block)
|
352
416
|
@channel.once_open do
|
353
417
|
self.once_name_is_available do
|
354
|
-
|
418
|
+
queue_unbind(exchange, (opts[:key] || opts[:routing_key] || AMQ::Protocol::EMPTY_STRING), opts[:arguments], &block)
|
355
419
|
end
|
356
420
|
end
|
357
421
|
end
|
@@ -379,7 +443,7 @@ module AMQP
|
|
379
443
|
# @return [NilClass] nil (for v0.7 compatibility)
|
380
444
|
#
|
381
445
|
# @yield [delete_ok] Yields AMQP method (queue.delete-ok) instance.
|
382
|
-
# @yieldparam [
|
446
|
+
# @yieldparam [AMQ::Protocol::Queue::DeleteOk] delete_ok AMQP queue.delete-ok) instance. Carries number of messages that were in the queue.
|
383
447
|
#
|
384
448
|
# @api public
|
385
449
|
# @see Queue#purge
|
@@ -387,7 +451,7 @@ module AMQP
|
|
387
451
|
def delete(opts = {}, &block)
|
388
452
|
@channel.once_open do
|
389
453
|
self.once_name_is_available do
|
390
|
-
|
454
|
+
queue_delete(opts.fetch(:if_unused, false), opts.fetch(:if_empty, false), opts.fetch(:nowait, false), &block)
|
391
455
|
end
|
392
456
|
end
|
393
457
|
|
@@ -406,7 +470,7 @@ module AMQP
|
|
406
470
|
#
|
407
471
|
#
|
408
472
|
# @yield [purge_ok] Yields AMQP method (queue.purge-ok) instance.
|
409
|
-
# @yieldparam [
|
473
|
+
# @yieldparam [AMQ::Protocol::Queue::PurgeOk] purge_ok AMQP queue.purge-ok) instance. Carries number of messages that were purged.
|
410
474
|
#
|
411
475
|
# @api public
|
412
476
|
# @see Queue#delete
|
@@ -414,7 +478,7 @@ module AMQP
|
|
414
478
|
def purge(opts = {}, &block)
|
415
479
|
@channel.once_open do
|
416
480
|
self.once_declared do
|
417
|
-
|
481
|
+
queue_purge(opts.fetch(:nowait, false), &block)
|
418
482
|
end
|
419
483
|
end
|
420
484
|
|
@@ -477,7 +541,7 @@ module AMQP
|
|
477
541
|
|
478
542
|
@channel.once_open do
|
479
543
|
self.once_name_is_available do
|
480
|
-
# see
|
544
|
+
# see AMQP::Queue#get in amq-client
|
481
545
|
self.get(!opts.fetch(:ack, false), &shim)
|
482
546
|
end
|
483
547
|
end
|
@@ -727,7 +791,7 @@ module AMQP
|
|
727
791
|
self.once_name_is_available do
|
728
792
|
# guards against a pathological case race condition when a channel
|
729
793
|
# is opened and closed before delayed operations are completed.
|
730
|
-
self.
|
794
|
+
self.basic_consume(!opts[:ack], opts[:exclusive], (opts[:nowait] || block.nil?), opts[:no_local], nil, &opts[:confirm])
|
731
795
|
|
732
796
|
self.on_delivery(&block)
|
733
797
|
end
|
@@ -736,6 +800,13 @@ module AMQP
|
|
736
800
|
self
|
737
801
|
end
|
738
802
|
|
803
|
+
# @api public
|
804
|
+
# @see http://bit.ly/amqp091reference AMQP 0.9.1 protocol reference (Sections 1.8.3.9)
|
805
|
+
def on_delivery(&block)
|
806
|
+
@default_consumer.on_delivery(&block)
|
807
|
+
end # on_delivery(&block)
|
808
|
+
|
809
|
+
|
739
810
|
# @return [String] Consumer tag of the default consumer associated with this queue (if any), or nil
|
740
811
|
# @note Default consumer is the one registered with the convenience {AMQP::Queue#subscribe} method. It has no special properties of any kind.
|
741
812
|
# @see Queue#subscribe
|
@@ -786,7 +857,7 @@ module AMQP
|
|
786
857
|
# method it will raise a channel or connection exception.
|
787
858
|
#
|
788
859
|
# @yield [cancel_ok]
|
789
|
-
# @yieldparam [
|
860
|
+
# @yieldparam [AMQ::Protocol::Basic::CancelOk] cancel_ok AMQP method basic.cancel-ok. You can obtain consumer tag from it.
|
790
861
|
#
|
791
862
|
#
|
792
863
|
# @api public
|
@@ -880,17 +951,453 @@ module AMQP
|
|
880
951
|
# @private
|
881
952
|
# @api plugin
|
882
953
|
def handle_connection_interruption(method = nil)
|
883
|
-
|
954
|
+
@consumers.each { |tag, consumer| consumer.handle_connection_interruption(method) }
|
955
|
+
|
956
|
+
self.exec_callback_yielding_self(:after_connection_interruption)
|
884
957
|
|
885
958
|
@declaration_deferrable = EventMachine::DefaultDeferrable.new
|
886
959
|
end
|
887
960
|
|
888
961
|
def handle_declare_ok(method)
|
889
|
-
|
962
|
+
@name = method.queue if @name.empty?
|
963
|
+
@channel.register_queue(self)
|
964
|
+
|
965
|
+
self.exec_callback_once_yielding_self(:declare, method)
|
966
|
+
|
890
967
|
@declaration_deferrable.succeed
|
891
968
|
end
|
892
969
|
|
893
970
|
|
971
|
+
# @group Declaration
|
972
|
+
|
973
|
+
# Declares this queue.
|
974
|
+
#
|
975
|
+
#
|
976
|
+
# @return [Queue] self
|
977
|
+
#
|
978
|
+
# @api public
|
979
|
+
# @see http://bit.ly/amqp091reference AMQP 0.9.1 protocol reference (Section 1.7.2.1.)
|
980
|
+
def queue_declare(passive = false, durable = false, exclusive = false, auto_delete = false, nowait = false, arguments = nil, &block)
|
981
|
+
raise ArgumentError, "declaration with nowait does not make sense for server-named queues! Either specify name other than empty string or use #declare without nowait" if nowait && self.anonymous?
|
982
|
+
|
983
|
+
# these two are for autorecovery. MK.
|
984
|
+
@passive = passive
|
985
|
+
@server_named = @name.empty?
|
986
|
+
|
987
|
+
@durable = durable
|
988
|
+
@exclusive = exclusive
|
989
|
+
@auto_delete = auto_delete
|
990
|
+
@arguments = arguments
|
991
|
+
|
992
|
+
nowait = true if !block && !@name.empty? && nowait.nil?
|
993
|
+
@connection.send_frame(AMQ::Protocol::Queue::Declare.encode(@channel.id, @name, passive, durable, exclusive, auto_delete, nowait, arguments))
|
994
|
+
|
995
|
+
if !nowait
|
996
|
+
self.append_callback(:declare, &block)
|
997
|
+
@channel.queues_awaiting_declare_ok.push(self)
|
998
|
+
end
|
999
|
+
|
1000
|
+
self
|
1001
|
+
end
|
1002
|
+
|
1003
|
+
# Re-declares queue with the same attributes
|
1004
|
+
# @api public
|
1005
|
+
def redeclare(&block)
|
1006
|
+
nowait = true if !block && !@name.empty?
|
1007
|
+
|
1008
|
+
# server-named queues get their new generated names.
|
1009
|
+
new_name = if @server_named
|
1010
|
+
AMQ::Protocol::EMPTY_STRING
|
1011
|
+
else
|
1012
|
+
@name
|
1013
|
+
end
|
1014
|
+
@connection.send_frame(AMQ::Protocol::Queue::Declare.encode(@channel.id, new_name, @passive, @durable, @exclusive, @auto_delete, false, @arguments))
|
1015
|
+
|
1016
|
+
if !nowait
|
1017
|
+
self.append_callback(:declare, &block)
|
1018
|
+
@channel.queues_awaiting_declare_ok.push(self)
|
1019
|
+
end
|
1020
|
+
|
1021
|
+
self
|
1022
|
+
end
|
1023
|
+
|
1024
|
+
# @endgroup
|
1025
|
+
|
1026
|
+
|
1027
|
+
|
1028
|
+
# Deletes this queue.
|
1029
|
+
#
|
1030
|
+
# @param [Boolean] if_unused delete only if queue has no consumers (subscribers).
|
1031
|
+
# @param [Boolean] if_empty delete only if queue has no messages in it.
|
1032
|
+
# @param [Boolean] nowait Don't wait for reply from broker.
|
1033
|
+
# @return [Queue] self
|
1034
|
+
#
|
1035
|
+
# @api public
|
1036
|
+
# @see http://bit.ly/amqp091reference AMQP 0.9.1 protocol reference (Section 1.7.2.9.)
|
1037
|
+
def queue_delete(if_unused = false, if_empty = false, nowait = false, &block)
|
1038
|
+
nowait = true unless block
|
1039
|
+
@connection.send_frame(AMQ::Protocol::Queue::Delete.encode(@channel.id, @name, if_unused, if_empty, nowait))
|
1040
|
+
|
1041
|
+
if !nowait
|
1042
|
+
self.append_callback(:delete, &block)
|
1043
|
+
|
1044
|
+
# TODO: delete itself from queues cache
|
1045
|
+
@channel.queues_awaiting_delete_ok.push(self)
|
1046
|
+
end
|
1047
|
+
|
1048
|
+
self
|
1049
|
+
end # delete(channel, queue, if_unused, if_empty, nowait, &block)
|
1050
|
+
|
1051
|
+
|
1052
|
+
|
1053
|
+
# @group Binding
|
1054
|
+
|
1055
|
+
#
|
1056
|
+
# @return [Queue] self
|
1057
|
+
#
|
1058
|
+
# @api public
|
1059
|
+
# @see http://bit.ly/amqp091reference AMQP 0.9.1 protocol reference (Section 1.7.2.3.)
|
1060
|
+
def queue_bind(exchange, routing_key = AMQ::Protocol::EMPTY_STRING, nowait = false, arguments = nil, &block)
|
1061
|
+
nowait = true unless block
|
1062
|
+
exchange_name = if exchange.respond_to?(:name)
|
1063
|
+
exchange.name
|
1064
|
+
else
|
1065
|
+
|
1066
|
+
exchange
|
1067
|
+
end
|
1068
|
+
|
1069
|
+
@connection.send_frame(AMQ::Protocol::Queue::Bind.encode(@channel.id, @name, exchange_name, routing_key, nowait, arguments))
|
1070
|
+
|
1071
|
+
if !nowait
|
1072
|
+
self.append_callback(:bind, &block)
|
1073
|
+
@channel.queues_awaiting_bind_ok.push(self)
|
1074
|
+
end
|
1075
|
+
|
1076
|
+
# store bindings for automatic recovery, but BE VERY CAREFUL to
|
1077
|
+
# not cause an infinite rebinding loop here when we recover. MK.
|
1078
|
+
binding = { :exchange => exchange_name, :routing_key => routing_key, :arguments => arguments }
|
1079
|
+
@bindings.push(binding) unless @bindings.include?(binding)
|
1080
|
+
|
1081
|
+
self
|
1082
|
+
end
|
1083
|
+
|
1084
|
+
#
|
1085
|
+
# @return [Queue] self
|
1086
|
+
#
|
1087
|
+
# @api public
|
1088
|
+
# @see http://bit.ly/amqp091reference AMQP 0.9.1 protocol reference (Section 1.7.2.5.)
|
1089
|
+
def queue_unbind(exchange, routing_key = AMQ::Protocol::EMPTY_STRING, arguments = nil, &block)
|
1090
|
+
exchange_name = if exchange.respond_to?(:name)
|
1091
|
+
exchange.name
|
1092
|
+
else
|
1093
|
+
|
1094
|
+
exchange
|
1095
|
+
end
|
1096
|
+
|
1097
|
+
@connection.send_frame(AMQ::Protocol::Queue::Unbind.encode(@channel.id, @name, exchange_name, routing_key, arguments))
|
1098
|
+
|
1099
|
+
self.append_callback(:unbind, &block)
|
1100
|
+
@channel.queues_awaiting_unbind_ok.push(self)
|
1101
|
+
|
1102
|
+
|
1103
|
+
@bindings.delete_if { |b| b[:exchange] == exchange_name }
|
1104
|
+
|
1105
|
+
self
|
1106
|
+
end
|
1107
|
+
|
1108
|
+
# @endgroup
|
1109
|
+
|
1110
|
+
|
1111
|
+
|
1112
|
+
|
1113
|
+
# @group Consuming messages
|
1114
|
+
|
1115
|
+
#
|
1116
|
+
# @return [Queue] self
|
1117
|
+
#
|
1118
|
+
# @api public
|
1119
|
+
# @see http://bit.ly/amqp091reference AMQP 0.9.1 protocol reference (Section 1.8.3.3.)
|
1120
|
+
def basic_consume(no_ack = false, exclusive = false, nowait = false, no_local = false, arguments = nil, &block)
|
1121
|
+
raise RuntimeError.new("This queue already has default consumer. Please instantiate AMQP::Consumer directly to register additional consumers.") if @default_consumer
|
1122
|
+
|
1123
|
+
nowait = true unless block
|
1124
|
+
@default_consumer = self.class.consumer_class.new(@channel, self, generate_consumer_tag(@name), exclusive, no_ack, arguments, no_local, &block)
|
1125
|
+
@default_consumer.consume(nowait, &block)
|
1126
|
+
|
1127
|
+
self
|
1128
|
+
end
|
1129
|
+
|
1130
|
+
# Unsubscribes from message delivery.
|
1131
|
+
# @return [Queue] self
|
1132
|
+
#
|
1133
|
+
# @api public
|
1134
|
+
# @see http://bit.ly/amqp091reference AMQP 0.9.1 protocol reference (Section 1.8.3.5.)
|
1135
|
+
def cancel(nowait = false, &block)
|
1136
|
+
raise "There is no default consumer for this queue. This usually means that you are trying to unsubscribe a queue that never was subscribed for messages in the first place." if @default_consumer.nil?
|
1137
|
+
|
1138
|
+
@default_consumer.cancel(nowait, &block)
|
1139
|
+
|
1140
|
+
self
|
1141
|
+
end # cancel(&block)
|
1142
|
+
|
1143
|
+
# @api public
|
1144
|
+
def on_cancel(&block)
|
1145
|
+
@default_consumer.on_cancel(&block)
|
1146
|
+
end # on_cancel(&block)
|
1147
|
+
|
1148
|
+
# @endgroup
|
1149
|
+
|
1150
|
+
|
1151
|
+
|
1152
|
+
|
1153
|
+
# @group Working With Messages
|
1154
|
+
|
1155
|
+
# Fetches messages from the queue.
|
1156
|
+
# @return [Queue] self
|
1157
|
+
#
|
1158
|
+
# @api public
|
1159
|
+
# @see http://bit.ly/amqp091reference AMQP 0.9.1 protocol reference (Section 1.8.3.10.)
|
1160
|
+
def get(no_ack = false, &block)
|
1161
|
+
@connection.send_frame(AMQ::Protocol::Basic::Get.encode(@channel.id, @name, no_ack))
|
1162
|
+
|
1163
|
+
# most people only want one callback per #get call. Consider the following example:
|
1164
|
+
#
|
1165
|
+
# 100.times { queue.get { ... } }
|
1166
|
+
#
|
1167
|
+
# most likely you won't expect 100 callback runs per message here. MK.
|
1168
|
+
self.redefine_callback(:get, &block)
|
1169
|
+
@channel.queues_awaiting_get_response.push(self)
|
1170
|
+
|
1171
|
+
self
|
1172
|
+
end # get(no_ack = false, &block)
|
1173
|
+
|
1174
|
+
|
1175
|
+
|
1176
|
+
# Purges (removes all messagse from) the queue.
|
1177
|
+
# @return [Queue] self
|
1178
|
+
#
|
1179
|
+
# @api public
|
1180
|
+
# @see http://bit.ly/amqp091reference AMQP 0.9.1 protocol reference (Section 1.7.2.7.)
|
1181
|
+
def queue_purge(nowait = false, &block)
|
1182
|
+
nowait = true unless block
|
1183
|
+
@connection.send_frame(AMQ::Protocol::Queue::Purge.encode(@channel.id, @name, nowait))
|
1184
|
+
|
1185
|
+
if !nowait
|
1186
|
+
self.redefine_callback(:purge, &block)
|
1187
|
+
# TODO: handle channel & connection-level exceptions
|
1188
|
+
@channel.queues_awaiting_purge_ok.push(self)
|
1189
|
+
end
|
1190
|
+
|
1191
|
+
self
|
1192
|
+
end # purge(nowait = false, &block)
|
1193
|
+
|
1194
|
+
# @endgroup
|
1195
|
+
|
1196
|
+
|
1197
|
+
|
1198
|
+
# @group Acknowledging & Rejecting Messages
|
1199
|
+
|
1200
|
+
# Acknowledge a delivery tag.
|
1201
|
+
# @return [Queue] self
|
1202
|
+
#
|
1203
|
+
# @api public
|
1204
|
+
# @see http://bit.ly/amqp091reference AMQP 0.9.1 protocol reference (Section 1.8.3.13.)
|
1205
|
+
def acknowledge(delivery_tag)
|
1206
|
+
@channel.acknowledge(delivery_tag)
|
1207
|
+
|
1208
|
+
self
|
1209
|
+
end # acknowledge(delivery_tag)
|
1210
|
+
|
1211
|
+
#
|
1212
|
+
# @return [Queue] self
|
1213
|
+
#
|
1214
|
+
# @api public
|
1215
|
+
# @see http://bit.ly/amqp091reference AMQP 0.9.1 protocol reference (Section 1.8.3.14.)
|
1216
|
+
def reject(delivery_tag, requeue = true)
|
1217
|
+
@channel.reject(delivery_tag, requeue)
|
1218
|
+
|
1219
|
+
self
|
1220
|
+
end # reject(delivery_tag, requeue = true)
|
1221
|
+
|
1222
|
+
# @endgroup
|
1223
|
+
|
1224
|
+
|
1225
|
+
|
1226
|
+
|
1227
|
+
# @group Error Handling & Recovery
|
1228
|
+
|
1229
|
+
# Defines a callback that will be executed after TCP connection is interrupted (typically because of a network failure).
|
1230
|
+
# Only one callback can be defined (the one defined last replaces previously added ones).
|
1231
|
+
#
|
1232
|
+
# @api public
|
1233
|
+
def on_connection_interruption(&block)
|
1234
|
+
self.redefine_callback(:after_connection_interruption, &block)
|
1235
|
+
end # on_connection_interruption(&block)
|
1236
|
+
alias after_connection_interruption on_connection_interruption
|
1237
|
+
|
1238
|
+
# Defines a callback that will be executed after TCP connection is recovered after a network failure
|
1239
|
+
# but before AMQP connection is re-opened.
|
1240
|
+
# Only one callback can be defined (the one defined last replaces previously added ones).
|
1241
|
+
#
|
1242
|
+
# @api public
|
1243
|
+
def before_recovery(&block)
|
1244
|
+
self.redefine_callback(:before_recovery, &block)
|
1245
|
+
end # before_recovery(&block)
|
1246
|
+
|
1247
|
+
# @private
|
1248
|
+
def run_before_recovery_callbacks
|
1249
|
+
self.exec_callback_yielding_self(:before_recovery)
|
1250
|
+
|
1251
|
+
@consumers.each { |tag, c| c.run_before_recovery_callbacks }
|
1252
|
+
end
|
1253
|
+
|
1254
|
+
|
1255
|
+
# Defines a callback that will be executed when AMQP connection is recovered after a network failure..
|
1256
|
+
# Only one callback can be defined (the one defined last replaces previously added ones).
|
1257
|
+
#
|
1258
|
+
# @api public
|
1259
|
+
def on_recovery(&block)
|
1260
|
+
self.redefine_callback(:after_recovery, &block)
|
1261
|
+
end # on_recovery(&block)
|
1262
|
+
alias after_recovery on_recovery
|
1263
|
+
|
1264
|
+
# @private
|
1265
|
+
def run_after_recovery_callbacks
|
1266
|
+
self.exec_callback_yielding_self(:after_recovery)
|
1267
|
+
|
1268
|
+
@consumers.each { |tag, c| c.run_after_recovery_callbacks }
|
1269
|
+
end
|
1270
|
+
|
1271
|
+
|
1272
|
+
|
1273
|
+
# Called by associated connection object when AMQP connection has been re-established
|
1274
|
+
# (for example, after a network failure).
|
1275
|
+
#
|
1276
|
+
# @api plugin
|
1277
|
+
def auto_recover
|
1278
|
+
self.exec_callback_yielding_self(:before_recovery)
|
1279
|
+
self.redeclare do
|
1280
|
+
self.rebind
|
1281
|
+
|
1282
|
+
@consumers.each { |tag, consumer| consumer.auto_recover }
|
1283
|
+
|
1284
|
+
self.exec_callback_yielding_self(:after_recovery)
|
1285
|
+
end
|
1286
|
+
end # auto_recover
|
1287
|
+
|
1288
|
+
# @endgroup
|
1289
|
+
|
1290
|
+
|
1291
|
+
#
|
1292
|
+
# Implementation
|
1293
|
+
#
|
1294
|
+
|
1295
|
+
|
1296
|
+
# Unique string supposed to be used as a consumer tag.
|
1297
|
+
#
|
1298
|
+
# @return [String] Unique string.
|
1299
|
+
# @api plugin
|
1300
|
+
def generate_consumer_tag(name)
|
1301
|
+
"#{name}-#{Time.now.to_i * 1000}-#{Kernel.rand(999_999_999_999)}"
|
1302
|
+
end
|
1303
|
+
|
1304
|
+
|
1305
|
+
def handle_connection_interruption(method = nil)
|
1306
|
+
@consumers.each { |tag, c| c.handle_connection_interruption(method) }
|
1307
|
+
end # handle_connection_interruption(method = nil)
|
1308
|
+
|
1309
|
+
|
1310
|
+
def handle_delete_ok(method)
|
1311
|
+
self.exec_callback_once(:delete, method)
|
1312
|
+
end # handle_delete_ok(method)
|
1313
|
+
|
1314
|
+
def handle_purge_ok(method)
|
1315
|
+
self.exec_callback_once(:purge, method)
|
1316
|
+
end # handle_purge_ok(method)
|
1317
|
+
|
1318
|
+
def handle_bind_ok(method)
|
1319
|
+
self.exec_callback_once(:bind, method)
|
1320
|
+
end # handle_bind_ok(method)
|
1321
|
+
|
1322
|
+
def handle_unbind_ok(method)
|
1323
|
+
self.exec_callback_once(:unbind, method)
|
1324
|
+
end # handle_unbind_ok(method)
|
1325
|
+
|
1326
|
+
def handle_get_ok(method, header, payload)
|
1327
|
+
method = AMQ::Protocol::GetResponse.new(method)
|
1328
|
+
self.exec_callback(:get, method, header, payload)
|
1329
|
+
end # handle_get_ok(method, header, payload)
|
1330
|
+
|
1331
|
+
def handle_get_empty(method)
|
1332
|
+
method = AMQ::Protocol::GetResponse.new(method)
|
1333
|
+
self.exec_callback(:get, method)
|
1334
|
+
end # handle_get_empty(method)
|
1335
|
+
|
1336
|
+
|
1337
|
+
|
1338
|
+
# Get the first queue which didn't receive Queue.Declare-Ok yet and run its declare callback.
|
1339
|
+
# The cache includes only queues with {nowait: false}.
|
1340
|
+
self.handle(AMQ::Protocol::Queue::DeclareOk) do |connection, frame|
|
1341
|
+
method = frame.decode_payload
|
1342
|
+
|
1343
|
+
channel = connection.channels[frame.channel]
|
1344
|
+
queue = channel.queues_awaiting_declare_ok.shift
|
1345
|
+
|
1346
|
+
queue.handle_declare_ok(method)
|
1347
|
+
end
|
1348
|
+
|
1349
|
+
|
1350
|
+
self.handle(AMQ::Protocol::Queue::DeleteOk) do |connection, frame|
|
1351
|
+
channel = connection.channels[frame.channel]
|
1352
|
+
queue = channel.queues_awaiting_delete_ok.shift
|
1353
|
+
queue.handle_delete_ok(frame.decode_payload)
|
1354
|
+
end
|
1355
|
+
|
1356
|
+
|
1357
|
+
self.handle(AMQ::Protocol::Queue::BindOk) do |connection, frame|
|
1358
|
+
channel = connection.channels[frame.channel]
|
1359
|
+
queue = channel.queues_awaiting_bind_ok.shift
|
1360
|
+
|
1361
|
+
queue.handle_bind_ok(frame.decode_payload)
|
1362
|
+
end
|
1363
|
+
|
1364
|
+
|
1365
|
+
self.handle(AMQ::Protocol::Queue::UnbindOk) do |connection, frame|
|
1366
|
+
channel = connection.channels[frame.channel]
|
1367
|
+
queue = channel.queues_awaiting_unbind_ok.shift
|
1368
|
+
|
1369
|
+
queue.handle_unbind_ok(frame.decode_payload)
|
1370
|
+
end
|
1371
|
+
|
1372
|
+
|
1373
|
+
self.handle(AMQ::Protocol::Queue::PurgeOk) do |connection, frame|
|
1374
|
+
channel = connection.channels[frame.channel]
|
1375
|
+
queue = channel.queues_awaiting_purge_ok.shift
|
1376
|
+
|
1377
|
+
queue.handle_purge_ok(frame.decode_payload)
|
1378
|
+
end
|
1379
|
+
|
1380
|
+
|
1381
|
+
self.handle(AMQ::Protocol::Basic::GetOk) do |connection, frame, content_frames|
|
1382
|
+
channel = connection.channels[frame.channel]
|
1383
|
+
queue = channel.queues_awaiting_get_response.shift
|
1384
|
+
method = frame.decode_payload
|
1385
|
+
|
1386
|
+
header = content_frames.shift
|
1387
|
+
body = content_frames.map {|frame| frame.payload }.join
|
1388
|
+
|
1389
|
+
queue.handle_get_ok(method, header, body) if queue
|
1390
|
+
end
|
1391
|
+
|
1392
|
+
|
1393
|
+
self.handle(AMQ::Protocol::Basic::GetEmpty) do |connection, frame|
|
1394
|
+
channel = connection.channels[frame.channel]
|
1395
|
+
queue = channel.queues_awaiting_get_response.shift
|
1396
|
+
|
1397
|
+
queue.handle_get_empty(frame.decode_payload)
|
1398
|
+
end
|
1399
|
+
|
1400
|
+
|
894
1401
|
protected
|
895
1402
|
|
896
1403
|
# @private
|