amqp 1.1.0.pre1 → 1.1.0.pre2
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.
- 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
|