amq-client 0.7.0.alpha35 → 0.8.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.rspec +0 -1
- data/.travis.yml +9 -3
- data/Gemfile +22 -12
- data/amq-client.gemspec +1 -1
- data/examples/coolio_adapter/example_helper.rb +2 -0
- data/examples/eventmachine_adapter/basic_consume_with_acknowledgements.rb +3 -3
- data/examples/eventmachine_adapter/{connection_failure_callback.rb → error_handling/connection_failure_callback.rb} +4 -8
- data/examples/eventmachine_adapter/{connection_failure_exception.rb → error_handling/connection_failure_exception.rb} +5 -9
- data/examples/eventmachine_adapter/{connection_loss_handler.rb → error_handling/connection_loss_handler_that_fails_over.rb} +12 -12
- data/examples/eventmachine_adapter/error_handling/connection_loss_handler_with_automatic_recovery.rb +85 -0
- data/examples/eventmachine_adapter/error_handling/connection_loss_handler_with_manual_recovery.rb +85 -0
- data/examples/eventmachine_adapter/error_handling/handling_a_channel_level_exception.rb +2 -5
- data/examples/eventmachine_adapter/example_helper.rb +2 -0
- data/examples/eventmachine_adapter/server_capabilities.rb +12 -0
- data/examples/eventmachine_adapter/tls/tls_without_peer_verification.rb +2 -2
- data/lib/amq/client/async/adapter.rb +170 -31
- data/lib/amq/client/async/adapters/coolio.rb +18 -1
- data/lib/amq/client/async/adapters/event_machine.rb +48 -32
- data/lib/amq/client/async/adapters/eventmachine.rb +3 -1
- data/lib/amq/client/async/callbacks.rb +9 -7
- data/lib/amq/client/async/channel.rb +113 -20
- data/lib/amq/client/async/consumer.rb +270 -0
- data/lib/amq/client/async/exchange.rb +137 -16
- data/lib/amq/client/async/extensions/rabbitmq/confirm.rb +4 -4
- data/lib/amq/client/async/queue.rb +217 -113
- data/lib/amq/client/callbacks.rb +2 -0
- data/lib/amq/client/consumer_tag_generator.rb +24 -0
- data/lib/amq/client/exceptions.rb +10 -6
- data/lib/amq/client/handlers_registry.rb +2 -0
- data/lib/amq/client/queue.rb +2 -0
- data/lib/amq/client/server_named_entity.rb +1 -8
- data/lib/amq/client/settings.rb +64 -2
- data/lib/amq/client/version.rb +3 -1
- data/spec/benchmarks/adapters.rb +2 -0
- data/spec/client/framing/io_frame_spec.rb +9 -6
- data/spec/integration/coolio/basic_ack_spec.rb +2 -0
- data/spec/integration/coolio/basic_cancel_spec.rb +2 -0
- data/spec/integration/coolio/basic_consume_spec.rb +58 -0
- data/spec/integration/coolio/basic_get_spec.rb +2 -0
- data/spec/integration/coolio/basic_return_spec.rb +2 -0
- data/spec/integration/coolio/channel_close_spec.rb +2 -0
- data/spec/integration/coolio/channel_flow_spec.rb +2 -0
- data/spec/integration/coolio/connection_close_spec.rb +2 -0
- data/spec/integration/coolio/connection_start_spec.rb +2 -0
- data/spec/integration/coolio/exchange_declare_spec.rb +8 -6
- data/spec/integration/coolio/spec_helper.rb +2 -0
- data/spec/integration/coolio/tx_commit_spec.rb +2 -1
- data/spec/integration/coolio/tx_rollback_spec.rb +1 -1
- data/spec/integration/eventmachine/basic_ack_spec.rb +3 -1
- data/spec/integration/eventmachine/basic_cancel_spec.rb +2 -0
- data/spec/integration/eventmachine/basic_consume_spec.rb +90 -6
- data/spec/integration/eventmachine/basic_get_spec.rb +2 -0
- data/spec/integration/eventmachine/basic_return_spec.rb +2 -0
- data/spec/integration/eventmachine/channel_close_spec.rb +2 -0
- data/spec/integration/eventmachine/channel_flow_spec.rb +4 -2
- data/spec/integration/eventmachine/concurrent_basic_publish_spec.rb +79 -0
- data/spec/integration/eventmachine/connection_close_spec.rb +2 -0
- data/spec/integration/eventmachine/connection_start_spec.rb +2 -0
- data/spec/integration/eventmachine/exchange_declare_spec.rb +4 -2
- data/spec/integration/eventmachine/queue_declare_spec.rb +2 -0
- data/spec/integration/eventmachine/regressions/amqp_gem_issue66_spec.rb +2 -0
- data/spec/integration/eventmachine/spec_helper.rb +2 -0
- data/spec/integration/eventmachine/tx_commit_spec.rb +2 -1
- data/spec/integration/eventmachine/tx_rollback_spec.rb +1 -1
- data/spec/regression/bad_frame_slicing_in_adapters_spec.rb +2 -0
- data/spec/spec_helper.rb +10 -0
- data/spec/unit/client/settings_spec.rb +92 -3
- metadata +24 -23
- data/CONTRIBUTORS +0 -3
@@ -1,5 +1,6 @@
|
|
1
1
|
# encoding: utf-8
|
2
2
|
|
3
|
+
require "amq/client/exceptions"
|
3
4
|
require "amq/client/entity"
|
4
5
|
require "amq/client/server_named_entity"
|
5
6
|
|
@@ -13,13 +14,8 @@ module AMQ
|
|
13
14
|
include ServerNamedEntity
|
14
15
|
extend ProtocolMethodHandlers
|
15
16
|
|
16
|
-
|
17
|
+
BUILTIN_TYPES = [:fanout, :direct, :topic, :headers].freeze
|
17
18
|
|
18
|
-
class IncompatibleExchangeTypeError < StandardError
|
19
|
-
def initialize(types, given)
|
20
|
-
super("#{given.inspect} exchange type is unknown. Standard types are #{TYPES.inspect}, custom exchange types must begin with x-, for example: x-recent-history")
|
21
|
-
end
|
22
|
-
end
|
23
19
|
|
24
20
|
|
25
21
|
#
|
@@ -30,45 +26,81 @@ module AMQ
|
|
30
26
|
attr_reader :channel
|
31
27
|
|
32
28
|
# Exchange name. May be server-generated or assigned directly.
|
29
|
+
# @return [String]
|
33
30
|
attr_reader :name
|
34
31
|
|
35
32
|
# @return [Symbol] One of :direct, :fanout, :topic, :headers
|
36
33
|
attr_reader :type
|
37
34
|
|
35
|
+
# @return [Hash] Additional arguments given on queue declaration. Typically used by AMQP extensions.
|
36
|
+
attr_reader :arguments
|
37
|
+
|
38
|
+
|
39
|
+
|
38
40
|
def initialize(connection, channel, name, type = :fanout)
|
39
|
-
if !(
|
40
|
-
raise
|
41
|
+
if !(BUILTIN_TYPES.include?(type.to_sym) || type.to_s =~ /^x-.+/i)
|
42
|
+
raise UnknownExchangeTypeError.new(BUILTIN_TYPES, type)
|
41
43
|
end
|
42
44
|
|
43
|
-
@connection
|
44
|
-
@channel
|
45
|
-
@name
|
46
|
-
@type
|
45
|
+
@connection = connection
|
46
|
+
@channel = channel
|
47
|
+
@name = name
|
48
|
+
@type = type
|
47
49
|
|
48
50
|
# register pre-declared exchanges
|
49
|
-
if @name == AMQ::Protocol::EMPTY_STRING || @name =~ /^amq\.(fanout|topic)/
|
51
|
+
if @name == AMQ::Protocol::EMPTY_STRING || @name =~ /^amq\.(direct|fanout|topic|match|headers)/
|
50
52
|
@channel.register_exchange(self)
|
51
53
|
end
|
52
54
|
|
53
55
|
super(connection)
|
54
56
|
end
|
55
57
|
|
56
|
-
|
58
|
+
# @return [Boolean] true if this exchange is of type `fanout`
|
59
|
+
# @api public
|
57
60
|
def fanout?
|
58
61
|
@type == :fanout
|
59
62
|
end
|
60
63
|
|
64
|
+
# @return [Boolean] true if this exchange is of type `direct`
|
65
|
+
# @api public
|
61
66
|
def direct?
|
62
67
|
@type == :direct
|
63
68
|
end
|
64
69
|
|
70
|
+
# @return [Boolean] true if this exchange is of type `topic`
|
71
|
+
# @api public
|
65
72
|
def topic?
|
66
73
|
@type == :topic
|
67
74
|
end
|
68
75
|
|
76
|
+
# @return [Boolean] true if this exchange is of type `headers`
|
77
|
+
# @api public
|
78
|
+
def headers?
|
79
|
+
@type == :headers
|
80
|
+
end
|
81
|
+
|
82
|
+
# @return [Boolean] true if this exchange is of a custom type (begins with x-)
|
83
|
+
# @api public
|
84
|
+
def custom_type?
|
85
|
+
@type.to_s =~ /^x-.+/i
|
86
|
+
end # custom_type?
|
87
|
+
|
88
|
+
# @return [Boolean] true if this exchange is a pre-defined one (amq.direct, amq.fanout, amq.match and so on)
|
89
|
+
def predefined?
|
90
|
+
@name && !!(@name =~ /^amq\.(direct|fanout|topic|headers|match)/i)
|
91
|
+
end # predefined?
|
69
92
|
|
70
93
|
|
94
|
+
# @group Declaration
|
95
|
+
|
96
|
+
# @api public
|
71
97
|
def declare(passive = false, durable = false, auto_delete = false, nowait = false, arguments = nil, &block)
|
98
|
+
# for re-declaration
|
99
|
+
@passive = passive
|
100
|
+
@durable = durable
|
101
|
+
@auto_delete = auto_delete
|
102
|
+
@arguments = arguments
|
103
|
+
|
72
104
|
@connection.send_frame(Protocol::Exchange::Declare.encode(@channel.id, @name, @type.to_s, passive, durable, auto_delete, false, nowait, arguments))
|
73
105
|
|
74
106
|
unless nowait
|
@@ -80,6 +112,23 @@ module AMQ
|
|
80
112
|
end
|
81
113
|
|
82
114
|
|
115
|
+
# @api public
|
116
|
+
def redeclare(&block)
|
117
|
+
nowait = block.nil?
|
118
|
+
@connection.send_frame(Protocol::Exchange::Declare.encode(@channel.id, @name, @type.to_s, @passive, @durable, @auto_delete, false, nowait, @arguments))
|
119
|
+
|
120
|
+
unless nowait
|
121
|
+
self.define_callback(:declare, &block)
|
122
|
+
@channel.exchanges_awaiting_declare_ok.push(self)
|
123
|
+
end
|
124
|
+
|
125
|
+
self
|
126
|
+
end # redeclare(&block)
|
127
|
+
|
128
|
+
# @endgroup
|
129
|
+
|
130
|
+
|
131
|
+
# @api public
|
83
132
|
def delete(if_unused = false, nowait = false, &block)
|
84
133
|
@connection.send_frame(Protocol::Exchange::Delete.encode(@channel.id, @name, if_unused, nowait))
|
85
134
|
|
@@ -94,22 +143,94 @@ module AMQ
|
|
94
143
|
end # delete(if_unused = false, nowait = false)
|
95
144
|
|
96
145
|
|
146
|
+
|
147
|
+
# @group Publishing Messages
|
148
|
+
|
149
|
+
# @api public
|
97
150
|
def publish(payload, routing_key = AMQ::Protocol::EMPTY_STRING, user_headers = {}, mandatory = false, immediate = false, frame_size = nil)
|
98
151
|
headers = { :priority => 0, :delivery_mode => 2, :content_type => "application/octet-stream" }.merge(user_headers)
|
99
|
-
@connection.send_frameset(Protocol::Basic::Publish.encode(@channel.id, payload, headers, @name, routing_key, mandatory, immediate, (frame_size || @connection.frame_max)))
|
152
|
+
@connection.send_frameset(Protocol::Basic::Publish.encode(@channel.id, payload, headers, @name, routing_key, mandatory, immediate, (frame_size || @connection.frame_max)), @channel)
|
100
153
|
|
101
154
|
self
|
102
155
|
end
|
103
156
|
|
104
157
|
|
158
|
+
# @api public
|
105
159
|
def on_return(&block)
|
106
160
|
self.redefine_callback(:return, &block)
|
107
161
|
|
108
162
|
self
|
109
163
|
end # on_return(&block)
|
110
164
|
|
165
|
+
# @endgroup
|
166
|
+
|
111
167
|
|
112
168
|
|
169
|
+
# @group Error Handling and Recovery
|
170
|
+
|
171
|
+
|
172
|
+
# Defines a callback that will be executed after TCP connection is interrupted (typically because of a network failure).
|
173
|
+
# Only one callback can be defined (the one defined last replaces previously added ones).
|
174
|
+
#
|
175
|
+
# @api public
|
176
|
+
def on_connection_interruption(&block)
|
177
|
+
self.redefine_callback(:after_connection_interruption, &block)
|
178
|
+
end # on_connection_interruption(&block)
|
179
|
+
alias after_connection_interruption on_connection_interruption
|
180
|
+
|
181
|
+
# @private
|
182
|
+
def handle_connection_interruption(method = nil)
|
183
|
+
self.exec_callback_yielding_self(:after_connection_interruption)
|
184
|
+
end # handle_connection_interruption
|
185
|
+
|
186
|
+
|
187
|
+
|
188
|
+
# Defines a callback that will be executed after TCP connection is recovered after a network failure
|
189
|
+
# but before AMQP connection is re-opened.
|
190
|
+
# Only one callback can be defined (the one defined last replaces previously added ones).
|
191
|
+
#
|
192
|
+
# @api public
|
193
|
+
def before_recovery(&block)
|
194
|
+
self.redefine_callback(:before_recovery, &block)
|
195
|
+
end # before_recovery(&block)
|
196
|
+
|
197
|
+
# @private
|
198
|
+
def run_before_recovery_callbacks
|
199
|
+
self.exec_callback_yielding_self(:before_recovery)
|
200
|
+
end
|
201
|
+
|
202
|
+
|
203
|
+
# Defines a callback that will be executed when AMQP connection is recovered after a network failure..
|
204
|
+
# Only one callback can be defined (the one defined last replaces previously added ones).
|
205
|
+
#
|
206
|
+
# @api public
|
207
|
+
def on_recovery(&block)
|
208
|
+
self.redefine_callback(:after_recovery, &block)
|
209
|
+
end # on_recovery(&block)
|
210
|
+
alias after_recovery on_recovery
|
211
|
+
|
212
|
+
# @private
|
213
|
+
def run_after_recovery_callbacks
|
214
|
+
self.exec_callback_yielding_self(:after_recovery)
|
215
|
+
end
|
216
|
+
|
217
|
+
|
218
|
+
# Called by associated connection object when AMQP connection has been re-established
|
219
|
+
# (for example, after a network failure).
|
220
|
+
#
|
221
|
+
# @api plugin
|
222
|
+
def auto_recover
|
223
|
+
self.redeclare unless predefined?
|
224
|
+
end # auto_recover
|
225
|
+
|
226
|
+
# @endgroup
|
227
|
+
|
228
|
+
|
229
|
+
|
230
|
+
#
|
231
|
+
# Implementation
|
232
|
+
#
|
233
|
+
|
113
234
|
|
114
235
|
def handle_declare_ok(method)
|
115
236
|
@name = method.exchange if self.anonymous?
|
@@ -151,7 +272,7 @@ module AMQ
|
|
151
272
|
exchange.exec_callback(:return, method, header, body)
|
152
273
|
end
|
153
274
|
|
154
|
-
end # Exchange
|
275
|
+
end # Exchange
|
155
276
|
end # Async
|
156
277
|
end # Client
|
157
278
|
end # AMQ
|
@@ -100,11 +100,11 @@ module AMQ
|
|
100
100
|
# @see #confirm
|
101
101
|
def confirm_select(nowait = false, &block)
|
102
102
|
if nowait && block
|
103
|
-
raise "
|
103
|
+
raise ArgumentError, "confirm.select with nowait = true and a callback makes no sense"
|
104
104
|
end
|
105
105
|
|
106
106
|
@uses_publisher_confirmations = true
|
107
|
-
self.redefine_callback(:confirm_select, &block)
|
107
|
+
self.redefine_callback(:confirm_select, &block) unless nowait
|
108
108
|
@connection.send_frame(Protocol::Confirm::Select.encode(@id, nowait))
|
109
109
|
|
110
110
|
self
|
@@ -220,9 +220,9 @@ module AMQ
|
|
220
220
|
# @see AMQ::Client::Exchange#publish
|
221
221
|
# @see AMQ::Client::Extensions::RabbitMQ::Channel#publisher_index
|
222
222
|
# @return [self] self
|
223
|
-
def publish(*args)
|
223
|
+
def publish(*args, &block)
|
224
224
|
super(*args)
|
225
|
-
@channel.after_publish(*args)
|
225
|
+
@channel.after_publish(*args, &block)
|
226
226
|
|
227
227
|
self
|
228
228
|
end # publish
|
@@ -3,7 +3,9 @@
|
|
3
3
|
require "amq/client/async/entity"
|
4
4
|
require "amq/client/adapter"
|
5
5
|
require "amq/client/server_named_entity"
|
6
|
+
|
6
7
|
require "amq/protocol/get_response"
|
8
|
+
require "amq/client/async/consumer"
|
7
9
|
|
8
10
|
module AMQ
|
9
11
|
module Client
|
@@ -24,13 +26,26 @@ module AMQ
|
|
24
26
|
#
|
25
27
|
|
26
28
|
# Qeueue name. May be server-generated or assigned directly.
|
29
|
+
# @return [String]
|
27
30
|
attr_reader :name
|
28
31
|
|
29
32
|
# Channel this queue belongs to.
|
33
|
+
# @return [AMQ::Client::Channel]
|
30
34
|
attr_reader :channel
|
31
35
|
|
32
|
-
#
|
33
|
-
attr_reader :
|
36
|
+
# @return [Array<Hash>] All consumers on this queue.
|
37
|
+
attr_reader :consumers
|
38
|
+
|
39
|
+
# @return [AMQ::Client::Consumer] Default consumer (registered with {Queue#consume}).
|
40
|
+
attr_reader :default_consumer
|
41
|
+
|
42
|
+
# @return [Hash] Additional arguments given on queue declaration. Typically used by AMQP extensions.
|
43
|
+
attr_reader :arguments
|
44
|
+
|
45
|
+
# @return [Array<Hash>]
|
46
|
+
attr_reader :bindings
|
47
|
+
|
48
|
+
|
34
49
|
|
35
50
|
# @param [AMQ::Client::Adapter] AMQ networking adapter to use.
|
36
51
|
# @param [AMQ::Client::Channel] AMQ channel this queue object uses.
|
@@ -41,18 +56,22 @@ module AMQ
|
|
41
56
|
|
42
57
|
super(connection)
|
43
58
|
|
44
|
-
@name
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
59
|
+
@name = name
|
60
|
+
# this has to stay true even after queue.declare-ok arrives. MK.
|
61
|
+
@server_named = @name.empty?
|
62
|
+
if @server_named
|
63
|
+
self.on_connection_interruption do
|
64
|
+
# server-named queue need to get new names after recovery. MK.
|
65
|
+
@name = AMQ::Protocol::EMPTY_STRING
|
66
|
+
end
|
51
67
|
end
|
52
68
|
|
53
|
-
|
54
|
-
|
55
|
-
|
69
|
+
@channel = channel
|
70
|
+
|
71
|
+
# primarily for autorecovery. MK.
|
72
|
+
@bindings = Array.new
|
73
|
+
|
74
|
+
@consumers = Hash.new
|
56
75
|
end
|
57
76
|
|
58
77
|
|
@@ -75,6 +94,8 @@ module AMQ
|
|
75
94
|
end # auto_delete?
|
76
95
|
|
77
96
|
|
97
|
+
# @group Declaration
|
98
|
+
|
78
99
|
# Declares this queue.
|
79
100
|
#
|
80
101
|
#
|
@@ -85,9 +106,14 @@ module AMQ
|
|
85
106
|
def declare(passive = false, durable = false, exclusive = false, auto_delete = false, nowait = false, arguments = nil, &block)
|
86
107
|
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?
|
87
108
|
|
109
|
+
# these two are for autorecovery. MK.
|
110
|
+
@passive = passive
|
111
|
+
@server_named = @name.empty?
|
112
|
+
|
88
113
|
@durable = durable
|
89
114
|
@exclusive = exclusive
|
90
115
|
@auto_delete = auto_delete
|
116
|
+
@arguments = arguments
|
91
117
|
|
92
118
|
nowait = true if !block && !@name.empty?
|
93
119
|
@connection.send_frame(Protocol::Queue::Declare.encode(@channel.id, @name, passive, durable, exclusive, auto_delete, nowait, arguments))
|
@@ -100,6 +126,31 @@ module AMQ
|
|
100
126
|
self
|
101
127
|
end
|
102
128
|
|
129
|
+
# Re-declares queue with the same attributes
|
130
|
+
# @api public
|
131
|
+
def redeclare(&block)
|
132
|
+
nowait = true if !block && !@name.empty?
|
133
|
+
|
134
|
+
# server-named queues get their new generated names.
|
135
|
+
new_name = if @server_named
|
136
|
+
AMQ::Protocol::EMPTY_STRING
|
137
|
+
else
|
138
|
+
@name
|
139
|
+
end
|
140
|
+
@connection.send_frame(Protocol::Queue::Declare.encode(@channel.id, new_name, @passive, @durable, @exclusive, @auto_delete, false, @arguments))
|
141
|
+
|
142
|
+
if !nowait
|
143
|
+
self.append_callback(:declare, &block)
|
144
|
+
@channel.queues_awaiting_declare_ok.push(self)
|
145
|
+
end
|
146
|
+
|
147
|
+
self
|
148
|
+
end
|
149
|
+
|
150
|
+
# @endgroup
|
151
|
+
|
152
|
+
|
153
|
+
|
103
154
|
# Deletes this queue.
|
104
155
|
#
|
105
156
|
# @param [Boolean] if_unused delete only if queue has no consumers (subscribers).
|
@@ -123,6 +174,10 @@ module AMQ
|
|
123
174
|
self
|
124
175
|
end # delete(channel, queue, if_unused, if_empty, nowait, &block)
|
125
176
|
|
177
|
+
|
178
|
+
|
179
|
+
# @group Binding
|
180
|
+
|
126
181
|
#
|
127
182
|
# @return [Queue] self
|
128
183
|
#
|
@@ -141,11 +196,14 @@ module AMQ
|
|
141
196
|
|
142
197
|
if !nowait
|
143
198
|
self.append_callback(:bind, &block)
|
144
|
-
|
145
|
-
# TODO: handle channel & connection-level exceptions
|
146
199
|
@channel.queues_awaiting_bind_ok.push(self)
|
147
200
|
end
|
148
201
|
|
202
|
+
# store bindings for automatic recovery, but BE VERY CAREFUL to
|
203
|
+
# not cause an infinite rebinding loop here when we recover. MK.
|
204
|
+
binding = { :exchange => exchange_name, :routing_key => routing_key, :arguments => arguments }
|
205
|
+
@bindings.push(binding) unless @bindings.include?(binding)
|
206
|
+
|
149
207
|
self
|
150
208
|
end
|
151
209
|
|
@@ -165,12 +223,34 @@ module AMQ
|
|
165
223
|
@connection.send_frame(Protocol::Queue::Unbind.encode(@channel.id, @name, exchange_name, routing_key, arguments))
|
166
224
|
|
167
225
|
self.append_callback(:unbind, &block)
|
168
|
-
# TODO: handle channel & connection-level exceptions
|
169
226
|
@channel.queues_awaiting_unbind_ok.push(self)
|
170
227
|
|
228
|
+
|
229
|
+
@bindings.delete_if { |b| b[:exchange] == exchange_name }
|
230
|
+
|
171
231
|
self
|
172
232
|
end
|
173
233
|
|
234
|
+
# Used by automatic recovery machinery.
|
235
|
+
# @private
|
236
|
+
# @api plugin
|
237
|
+
def rebind(&block)
|
238
|
+
@bindings.each { |b| self.bind(b[:exchange], b[:routing_key], true, b[:arguments]) }
|
239
|
+
end
|
240
|
+
|
241
|
+
# @endgroup
|
242
|
+
|
243
|
+
|
244
|
+
|
245
|
+
|
246
|
+
# @group Consuming messages
|
247
|
+
|
248
|
+
# @return [Class] AMQ::Client::Consumer or other class implementing consumer API. Used by libraries like {https://github.com/ruby-amqp/amqp Ruby amqp gem}.
|
249
|
+
# @api plugin
|
250
|
+
def self.consumer_class
|
251
|
+
AMQ::Client::Async::Consumer
|
252
|
+
end # self.consumer_class
|
253
|
+
|
174
254
|
|
175
255
|
#
|
176
256
|
# @return [Queue] self
|
@@ -178,46 +258,44 @@ module AMQ
|
|
178
258
|
# @api public
|
179
259
|
# @see http://bit.ly/htCzCX AMQP 0.9.1 protocol documentation (Section 1.8.3.3.)
|
180
260
|
def consume(no_ack = false, exclusive = false, nowait = false, no_local = false, arguments = nil, &block)
|
181
|
-
raise RuntimeError.new("This
|
261
|
+
raise RuntimeError.new("This queue already has default consumer. Please instantiate AMQ::Client::Consumer directly to register additional consumers.") if @default_consumer
|
182
262
|
|
183
|
-
nowait
|
184
|
-
@
|
185
|
-
@
|
263
|
+
nowait = true unless block
|
264
|
+
@default_consumer = self.class.consumer_class.new(@channel, self, generate_consumer_tag(@name), exclusive, no_ack, arguments, no_local, &block)
|
265
|
+
@default_consumer.consume(nowait, &block)
|
186
266
|
|
187
|
-
|
267
|
+
self
|
268
|
+
end
|
188
269
|
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
|
270
|
+
# Unsubscribes from message delivery.
|
271
|
+
# @return [Queue] self
|
272
|
+
#
|
273
|
+
# @api public
|
274
|
+
# @see http://bit.ly/htCzCX AMQP 0.9.1 protocol documentation (Section 1.8.3.5.)
|
275
|
+
def cancel(nowait = false, &block)
|
276
|
+
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?
|
193
277
|
|
194
|
-
|
195
|
-
end
|
278
|
+
@default_consumer.cancel(nowait, &block)
|
196
279
|
|
197
280
|
self
|
198
|
-
end
|
281
|
+
end # cancel(&block)
|
199
282
|
|
200
|
-
#
|
201
|
-
#
|
202
|
-
# @return [String] Unique string.
|
203
|
-
# @api plugin
|
204
|
-
def generate_consumer_tag(name)
|
205
|
-
"#{name}-#{Time.now.to_i * 1000}-#{Kernel.rand(999_999_999_999)}"
|
206
|
-
end
|
283
|
+
# @endgroup
|
207
284
|
|
208
|
-
# Resets consumer tag by setting it to nil.
|
209
|
-
# @return [String] Consumer tag this queue previously used.
|
210
|
-
#
|
211
|
-
# @api plugin
|
212
|
-
def reset_consumer_tag!
|
213
|
-
ct = @consumer_tag.dup
|
214
|
-
@consumer_tag = nil
|
215
285
|
|
216
|
-
ct
|
217
|
-
end
|
218
286
|
|
219
287
|
|
220
|
-
#
|
288
|
+
# @group Working With Messages
|
289
|
+
|
290
|
+
|
291
|
+
# @api public
|
292
|
+
# @see http://bit.ly/htCzCX AMQP 0.9.1 protocol documentation (Sections 1.8.3.9)
|
293
|
+
def on_delivery(&block)
|
294
|
+
@default_consumer.on_delivery(&block)
|
295
|
+
end # on_delivery(&block)
|
296
|
+
|
297
|
+
|
298
|
+
# Fetches messages from the queue.
|
221
299
|
# @return [Queue] self
|
222
300
|
#
|
223
301
|
# @api public
|
@@ -236,28 +314,9 @@ module AMQ
|
|
236
314
|
self
|
237
315
|
end # get(no_ack = false, &block)
|
238
316
|
|
239
|
-
#
|
240
|
-
# @return [Queue] self
|
241
|
-
#
|
242
|
-
# @api public
|
243
|
-
# @see http://bit.ly/htCzCX AMQP 0.9.1 protocol documentation (Section 1.8.3.5.)
|
244
|
-
def cancel(nowait = false, &block)
|
245
|
-
raise "There is no consumer tag 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 @consumer_tag.nil?
|
246
|
-
|
247
|
-
@connection.send_frame(Protocol::Basic::Cancel.encode(@channel.id, @consumer_tag, nowait))
|
248
|
-
@consumer_tag = nil
|
249
|
-
self.clear_callbacks(:delivery)
|
250
|
-
self.clear_callbacks(:consume)
|
251
317
|
|
252
|
-
if !nowait
|
253
|
-
self.redefine_callback(:cancel, &block)
|
254
|
-
@channel.queues_awaiting_cancel_ok.push(self)
|
255
|
-
end
|
256
318
|
|
257
|
-
|
258
|
-
end # cancel(&block)
|
259
|
-
|
260
|
-
#
|
319
|
+
# Purges (removes all messagse from) the queue.
|
261
320
|
# @return [Queue] self
|
262
321
|
#
|
263
322
|
# @api public
|
@@ -275,7 +334,13 @@ module AMQ
|
|
275
334
|
self
|
276
335
|
end # purge(nowait = false, &block)
|
277
336
|
|
278
|
-
#
|
337
|
+
# @endgroup
|
338
|
+
|
339
|
+
|
340
|
+
|
341
|
+
# @group Acknowledging & Rejecting Messages
|
342
|
+
|
343
|
+
# Acknowledge a delivery tag.
|
279
344
|
# @return [Queue] self
|
280
345
|
#
|
281
346
|
# @api public
|
@@ -297,22 +362,103 @@ module AMQ
|
|
297
362
|
self
|
298
363
|
end # reject(delivery_tag, requeue = true)
|
299
364
|
|
365
|
+
# @endgroup
|
366
|
+
|
367
|
+
|
300
368
|
|
301
369
|
|
370
|
+
# @group Error Handling & Recovery
|
371
|
+
|
372
|
+
# Defines a callback that will be executed after TCP connection is interrupted (typically because of a network failure).
|
373
|
+
# Only one callback can be defined (the one defined last replaces previously added ones).
|
374
|
+
#
|
302
375
|
# @api public
|
303
|
-
|
304
|
-
|
305
|
-
|
306
|
-
|
376
|
+
def on_connection_interruption(&block)
|
377
|
+
self.redefine_callback(:after_connection_interruption, &block)
|
378
|
+
end # on_connection_interruption(&block)
|
379
|
+
alias after_connection_interruption on_connection_interruption
|
380
|
+
|
381
|
+
# @private
|
382
|
+
def handle_connection_interruption(method = nil)
|
383
|
+
@consumers.each { |tag, consumer| consumer.handle_connection_interruption(method) }
|
384
|
+
|
385
|
+
self.exec_callback_yielding_self(:after_connection_interruption)
|
386
|
+
end # handle_connection_interruption
|
387
|
+
|
388
|
+
# Defines a callback that will be executed after TCP connection is recovered after a network failure
|
389
|
+
# but before AMQP connection is re-opened.
|
390
|
+
# Only one callback can be defined (the one defined last replaces previously added ones).
|
391
|
+
#
|
392
|
+
# @api public
|
393
|
+
def before_recovery(&block)
|
394
|
+
self.redefine_callback(:before_recovery, &block)
|
395
|
+
end # before_recovery(&block)
|
396
|
+
|
397
|
+
# @private
|
398
|
+
def run_before_recovery_callbacks
|
399
|
+
self.exec_callback_yielding_self(:before_recovery)
|
400
|
+
|
401
|
+
@consumers.each { |tag, c| c.run_before_recovery_callbacks }
|
402
|
+
end
|
403
|
+
|
404
|
+
|
405
|
+
# Defines a callback that will be executed when AMQP connection is recovered after a network failure..
|
406
|
+
# Only one callback can be defined (the one defined last replaces previously added ones).
|
407
|
+
#
|
408
|
+
# @api public
|
409
|
+
def on_recovery(&block)
|
410
|
+
self.redefine_callback(:after_recovery, &block)
|
411
|
+
end # on_recovery(&block)
|
412
|
+
alias after_recovery on_recovery
|
413
|
+
|
414
|
+
# @private
|
415
|
+
def run_after_recovery_callbacks
|
416
|
+
self.exec_callback_yielding_self(:after_recovery)
|
417
|
+
|
418
|
+
@consumers.each { |tag, c| c.run_after_recovery_callbacks }
|
419
|
+
end
|
420
|
+
|
421
|
+
|
422
|
+
|
423
|
+
# Called by associated connection object when AMQP connection has been re-established
|
424
|
+
# (for example, after a network failure).
|
425
|
+
#
|
426
|
+
# @api plugin
|
427
|
+
def auto_recover
|
428
|
+
self.exec_callback_yielding_self(:before_recovery)
|
429
|
+
self.redeclare do
|
430
|
+
self.rebind
|
431
|
+
|
432
|
+
@consumers.each { |tag, consumer| consumer.auto_recover }
|
307
433
|
|
434
|
+
self.exec_callback_yielding_self(:after_recovery)
|
435
|
+
end
|
436
|
+
end # auto_recover
|
437
|
+
|
438
|
+
# @endgroup
|
308
439
|
|
309
440
|
|
310
441
|
#
|
311
442
|
# Implementation
|
312
443
|
#
|
313
444
|
|
445
|
+
|
446
|
+
# Unique string supposed to be used as a consumer tag.
|
447
|
+
#
|
448
|
+
# @return [String] Unique string.
|
449
|
+
# @api plugin
|
450
|
+
def generate_consumer_tag(name)
|
451
|
+
"#{name}-#{Time.now.to_i * 1000}-#{Kernel.rand(999_999_999_999)}"
|
452
|
+
end
|
453
|
+
|
454
|
+
|
455
|
+
def handle_connection_interruption(method = nil)
|
456
|
+
@consumers.each { |tag, c| c.handle_connection_interruption(method) }
|
457
|
+
end # handle_connection_interruption(method = nil)
|
458
|
+
|
459
|
+
|
314
460
|
def handle_declare_ok(method)
|
315
|
-
@name = method.queue if
|
461
|
+
@name = method.queue if @name.empty?
|
316
462
|
@channel.register_queue(self)
|
317
463
|
|
318
464
|
self.exec_callback_once_yielding_self(:declare, method)
|
@@ -322,10 +468,6 @@ module AMQ
|
|
322
468
|
self.exec_callback_once(:delete, method)
|
323
469
|
end # handle_delete_ok(method)
|
324
470
|
|
325
|
-
def handle_consume_ok(method)
|
326
|
-
self.exec_callback_once(:consume, method)
|
327
|
-
end # handle_consume_ok(method)
|
328
|
-
|
329
471
|
def handle_purge_ok(method)
|
330
472
|
self.exec_callback_once(:purge, method)
|
331
473
|
end # handle_purge_ok(method)
|
@@ -338,15 +480,6 @@ module AMQ
|
|
338
480
|
self.exec_callback_once(:unbind, method)
|
339
481
|
end # handle_unbind_ok(method)
|
340
482
|
|
341
|
-
def handle_delivery(method, header, payload)
|
342
|
-
self.exec_callback(:delivery, method, header, payload)
|
343
|
-
end # handle_delivery
|
344
|
-
|
345
|
-
def handle_cancel_ok(method)
|
346
|
-
@consumer_tag = nil
|
347
|
-
self.exec_callback_once(:cancel, method)
|
348
|
-
end # handle_cancel_ok(method)
|
349
|
-
|
350
483
|
def handle_get_ok(method, header, payload)
|
351
484
|
method = Protocol::GetResponse.new(method)
|
352
485
|
self.exec_callback(:get, method, header, payload)
|
@@ -394,35 +527,6 @@ module AMQ
|
|
394
527
|
end
|
395
528
|
|
396
529
|
|
397
|
-
self.handle(Protocol::Basic::ConsumeOk) do |connection, frame|
|
398
|
-
channel = connection.channels[frame.channel]
|
399
|
-
queue = channel.queues_awaiting_consume_ok.shift
|
400
|
-
|
401
|
-
queue.handle_consume_ok(frame.decode_payload)
|
402
|
-
end
|
403
|
-
|
404
|
-
|
405
|
-
self.handle(Protocol::Basic::CancelOk) do |connection, frame|
|
406
|
-
channel = connection.channels[frame.channel]
|
407
|
-
queue = channel.queues_awaiting_cancel_ok.shift
|
408
|
-
|
409
|
-
queue.handle_consume_ok(frame.decode_payload)
|
410
|
-
end
|
411
|
-
|
412
|
-
|
413
|
-
# Basic.Deliver
|
414
|
-
self.handle(Protocol::Basic::Deliver) do |connection, method_frame, content_frames|
|
415
|
-
channel = connection.channels[method_frame.channel]
|
416
|
-
method = method_frame.decode_payload
|
417
|
-
queue = channel.consumers[method.consumer_tag]
|
418
|
-
|
419
|
-
header = content_frames.shift
|
420
|
-
body = content_frames.map { |frame| frame.payload }.join
|
421
|
-
queue.handle_delivery(method, header, body)
|
422
|
-
# TODO: ack if necessary
|
423
|
-
end
|
424
|
-
|
425
|
-
|
426
530
|
self.handle(Protocol::Queue::PurgeOk) do |connection, frame|
|
427
531
|
channel = connection.channels[frame.channel]
|
428
532
|
queue = channel.queues_awaiting_purge_ok.shift
|
@@ -449,7 +553,7 @@ module AMQ
|
|
449
553
|
|
450
554
|
queue.handle_get_empty(frame.decode_payload)
|
451
555
|
end
|
452
|
-
end # Queue
|
556
|
+
end # Queue
|
453
557
|
end # Async
|
454
558
|
end # Client
|
455
559
|
end # AMQ
|