amq-client 0.7.0.alpha35 → 0.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/.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
|