amq-client 0.7.0.alpha34 → 0.7.0.alpha35
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/.travis.yml +4 -0
- data/Gemfile +1 -1
- data/README.textile +1 -1
- data/bin/ci/before_build.sh +24 -0
- data/examples/eventmachine_adapter/extensions/rabbitmq/handling_confirm_select_ok.rb +2 -2
- data/examples/eventmachine_adapter/extensions/rabbitmq/publisher_confirmations_with_transient_messages.rb +1 -1
- data/examples/eventmachine_adapter/extensions/rabbitmq/publisher_confirmations_with_unroutable_message.rb +1 -1
- data/lib/amq/client.rb +29 -17
- data/lib/amq/client/adapter.rb +8 -504
- data/lib/amq/client/adapters/coolio.rb +4 -282
- data/lib/amq/client/adapters/event_machine.rb +4 -382
- data/lib/amq/client/async/adapter.rb +517 -0
- data/lib/amq/client/async/adapters/coolio.rb +291 -0
- data/lib/amq/client/async/adapters/event_machine.rb +392 -0
- data/lib/amq/client/async/adapters/eventmachine.rb +1 -0
- data/lib/amq/client/async/callbacks.rb +71 -0
- data/lib/amq/client/async/channel.rb +385 -0
- data/lib/amq/client/async/entity.rb +66 -0
- data/lib/amq/client/async/exchange.rb +157 -0
- data/lib/amq/client/async/extensions/rabbitmq/basic.rb +38 -0
- data/lib/amq/client/async/extensions/rabbitmq/confirm.rb +248 -0
- data/lib/amq/client/async/queue.rb +455 -0
- data/lib/amq/client/callbacks.rb +6 -65
- data/lib/amq/client/channel.rb +4 -376
- data/lib/amq/client/entity.rb +6 -57
- data/lib/amq/client/exchange.rb +4 -148
- data/lib/amq/client/extensions/rabbitmq/basic.rb +4 -28
- data/lib/amq/client/extensions/rabbitmq/confirm.rb +5 -240
- data/lib/amq/client/queue.rb +5 -450
- data/lib/amq/client/version.rb +1 -1
- data/spec/unit/client_spec.rb +10 -30
- metadata +16 -22
@@ -0,0 +1 @@
|
|
1
|
+
require "amq/client/async/adapters/event_machine"
|
@@ -0,0 +1,71 @@
|
|
1
|
+
module AMQ
|
2
|
+
module Client
|
3
|
+
module Async
|
4
|
+
module Callbacks
|
5
|
+
|
6
|
+
def redefine_callback(event, callable = nil, &block)
|
7
|
+
f = (callable || block)
|
8
|
+
# yes, re-assign!
|
9
|
+
@callbacks[event] = [f]
|
10
|
+
|
11
|
+
self
|
12
|
+
end
|
13
|
+
|
14
|
+
def define_callback(event, callable = nil, &block)
|
15
|
+
f = (callable || block)
|
16
|
+
|
17
|
+
@callbacks[event] ||= []
|
18
|
+
@callbacks[event] << f if f
|
19
|
+
|
20
|
+
self
|
21
|
+
end # define_callback(event, &block)
|
22
|
+
alias append_callback define_callback
|
23
|
+
|
24
|
+
def prepend_callback(event, &block)
|
25
|
+
@callbacks[event] ||= []
|
26
|
+
@callbacks[event].unshift(block)
|
27
|
+
|
28
|
+
self
|
29
|
+
end # prepend_callback(event, &block)
|
30
|
+
|
31
|
+
def clear_callbacks(event)
|
32
|
+
@callbacks[event].clear if @callbacks[event]
|
33
|
+
end # clear_callbacks(event)
|
34
|
+
|
35
|
+
|
36
|
+
def exec_callback(name, *args, &block)
|
37
|
+
list = Array(self.callbacks[name])
|
38
|
+
if list.any?
|
39
|
+
list.each { |c| c.call(*args, &block) }
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
def exec_callback_once(name, *args, &block)
|
44
|
+
list = (self.callbacks.delete(name) || Array.new)
|
45
|
+
if list.any?
|
46
|
+
list.each { |c| c.call(*args, &block) }
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
def exec_callback_yielding_self(name, *args, &block)
|
51
|
+
list = Array(self.callbacks[name])
|
52
|
+
if list.any?
|
53
|
+
list.each { |c| c.call(self, *args, &block) }
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
def exec_callback_once_yielding_self(name, *args, &block)
|
58
|
+
list = (self.callbacks.delete(name) || Array.new)
|
59
|
+
|
60
|
+
if list.any?
|
61
|
+
list.each { |c| c.call(self, *args, &block) }
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
def has_callback?(name)
|
66
|
+
self.callbacks[name] && !self.callbacks[name].empty?
|
67
|
+
end # has_callback?
|
68
|
+
end # Callbacks
|
69
|
+
end # Async
|
70
|
+
end # Client
|
71
|
+
end # AMQ
|
@@ -0,0 +1,385 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require "amq/client/async/entity"
|
4
|
+
require "amq/client/queue"
|
5
|
+
require "amq/client/exchange"
|
6
|
+
|
7
|
+
module AMQ
|
8
|
+
module Client
|
9
|
+
module Async
|
10
|
+
class Channel
|
11
|
+
|
12
|
+
#
|
13
|
+
# Behaviors
|
14
|
+
#
|
15
|
+
|
16
|
+
extend RegisterEntityMixin
|
17
|
+
include Entity
|
18
|
+
extend ProtocolMethodHandlers
|
19
|
+
|
20
|
+
register_entity :queue, AMQ::Client::Queue
|
21
|
+
register_entity :exchange, AMQ::Client::Exchange
|
22
|
+
|
23
|
+
#
|
24
|
+
# API
|
25
|
+
#
|
26
|
+
|
27
|
+
|
28
|
+
class ChannelOutOfBadError < StandardError # TODO: inherit from some AMQP error class defined in amq-protocol or use it straight away.
|
29
|
+
def initialize(max, given)
|
30
|
+
super("Channel max is #{max}, #{given} given.")
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
|
35
|
+
DEFAULT_REPLY_TEXT = "Goodbye".freeze
|
36
|
+
|
37
|
+
attr_reader :id
|
38
|
+
|
39
|
+
attr_reader :exchanges_awaiting_declare_ok, :exchanges_awaiting_delete_ok
|
40
|
+
attr_reader :queues_awaiting_declare_ok, :queues_awaiting_delete_ok, :queues_awaiting_bind_ok, :queues_awaiting_unbind_ok, :queues_awaiting_purge_ok, :queues_awaiting_consume_ok, :queues_awaiting_cancel_ok, :queues_awaiting_get_response
|
41
|
+
|
42
|
+
attr_accessor :flow_is_active
|
43
|
+
|
44
|
+
|
45
|
+
def initialize(connection, id)
|
46
|
+
super(connection)
|
47
|
+
|
48
|
+
@id = id
|
49
|
+
@exchanges = Hash.new
|
50
|
+
@queues = Hash.new
|
51
|
+
@consumers = Hash.new
|
52
|
+
|
53
|
+
reset_state!
|
54
|
+
|
55
|
+
# 65536 is here for cases when channel is opened without passing a callback in,
|
56
|
+
# otherwise channel_mix would be nil and it causes a lot of needless headaches.
|
57
|
+
# lets just have this default. MK.
|
58
|
+
channel_max = if @connection.open?
|
59
|
+
@connection.channel_max || 65536
|
60
|
+
else
|
61
|
+
65536
|
62
|
+
end
|
63
|
+
|
64
|
+
if channel_max != 0 && !(0..channel_max).include?(id)
|
65
|
+
raise ChannelOutOfBadError.new(channel_max, id)
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
def consumers
|
70
|
+
@consumers
|
71
|
+
end # consumers
|
72
|
+
|
73
|
+
# @return [Array<Queue>] Collection of queues that were declared on this channel.
|
74
|
+
def queues
|
75
|
+
@queues.values
|
76
|
+
end
|
77
|
+
|
78
|
+
# @return [Array<Exchange>] Collection of exchanges that were declared on this channel.
|
79
|
+
def exchanges
|
80
|
+
@exchanges.values
|
81
|
+
end
|
82
|
+
|
83
|
+
|
84
|
+
# AMQP connection this channel belongs to.
|
85
|
+
#
|
86
|
+
# @return [AMQ::Client::Connection] Connection this channel belongs to.
|
87
|
+
def connection
|
88
|
+
@connection
|
89
|
+
end # connection
|
90
|
+
|
91
|
+
|
92
|
+
# @group Channel lifecycle
|
93
|
+
|
94
|
+
# Opens AMQP channel.
|
95
|
+
#
|
96
|
+
# @api public
|
97
|
+
def open(&block)
|
98
|
+
@connection.send_frame(Protocol::Channel::Open.encode(@id, AMQ::Protocol::EMPTY_STRING))
|
99
|
+
@connection.channels[@id] = self
|
100
|
+
self.status = :opening
|
101
|
+
|
102
|
+
self.redefine_callback :open, &block
|
103
|
+
end
|
104
|
+
|
105
|
+
# Closes AMQP channel.
|
106
|
+
#
|
107
|
+
# @api public
|
108
|
+
def close(reply_code = 200, reply_text = DEFAULT_REPLY_TEXT, class_id = 0, method_id = 0, &block)
|
109
|
+
@connection.send_frame(Protocol::Channel::Close.encode(@id, reply_code, reply_text, class_id, method_id))
|
110
|
+
|
111
|
+
self.redefine_callback :close, &block
|
112
|
+
end
|
113
|
+
|
114
|
+
# @endgroup
|
115
|
+
|
116
|
+
|
117
|
+
|
118
|
+
# @group Message acknowledgements
|
119
|
+
|
120
|
+
# Acknowledge one or all messages on the channel.
|
121
|
+
#
|
122
|
+
# @api public
|
123
|
+
# @see http://bit.ly/htCzCX AMQP 0.9.1 protocol documentation (Section 1.8.3.13.)
|
124
|
+
def acknowledge(delivery_tag, multiple = false)
|
125
|
+
@connection.send_frame(Protocol::Basic::Ack.encode(self.id, delivery_tag, multiple))
|
126
|
+
|
127
|
+
self
|
128
|
+
end # acknowledge(delivery_tag, multiple = false)
|
129
|
+
|
130
|
+
# Reject a message with given delivery tag.
|
131
|
+
#
|
132
|
+
# @api public
|
133
|
+
# @see http://bit.ly/htCzCX AMQP 0.9.1 protocol documentation (Section 1.8.3.14.)
|
134
|
+
def reject(delivery_tag, requeue = true)
|
135
|
+
@connection.send_frame(Protocol::Basic::Reject.encode(self.id, delivery_tag, requeue))
|
136
|
+
|
137
|
+
self
|
138
|
+
end # reject(delivery_tag, requeue = true)
|
139
|
+
|
140
|
+
# Notifies AMQ broker that consumer has recovered and unacknowledged messages need
|
141
|
+
# to be redelivered.
|
142
|
+
#
|
143
|
+
# @return [Channel] self
|
144
|
+
#
|
145
|
+
# @note RabbitMQ as of 2.3.1 does not support basic.recover with requeue = false.
|
146
|
+
# @see http://bit.ly/htCzCX AMQP 0.9.1 protocol documentation (Section 1.8.3.16.)
|
147
|
+
# @api public
|
148
|
+
def recover(requeue = true, &block)
|
149
|
+
@connection.send_frame(Protocol::Basic::Recover.encode(@id, requeue))
|
150
|
+
|
151
|
+
self.redefine_callback :recover, &block
|
152
|
+
self
|
153
|
+
end # recover(requeue = false, &block)
|
154
|
+
|
155
|
+
# @endgroup
|
156
|
+
|
157
|
+
|
158
|
+
|
159
|
+
# @group QoS and flow handling
|
160
|
+
|
161
|
+
# Requests a specific quality of service. The QoS can be specified for the current channel
|
162
|
+
# or for all channels on the connection.
|
163
|
+
#
|
164
|
+
# @note RabbitMQ as of 2.3.1 does not support prefetch_size.
|
165
|
+
# @api public
|
166
|
+
def qos(prefetch_size = 0, prefetch_count = 32, global = false, &block)
|
167
|
+
@connection.send_frame(Protocol::Basic::Qos.encode(@id, prefetch_size, prefetch_count, global))
|
168
|
+
|
169
|
+
self.redefine_callback :qos, &block
|
170
|
+
self
|
171
|
+
end # qos(prefetch_size = 4096, prefetch_count = 32, global = false, &block)
|
172
|
+
|
173
|
+
# Asks the peer to pause or restart the flow of content data sent to a consumer.
|
174
|
+
# This is a simple flowcontrol mechanism that a peer can use to avoid overflowing its
|
175
|
+
# queues or otherwise finding itself receiving more messages than it can process. Note that
|
176
|
+
# this method is not intended for window control. It does not affect contents returned to
|
177
|
+
# Queue#get callers.
|
178
|
+
#
|
179
|
+
# @param [Boolean] active Desired flow state.
|
180
|
+
#
|
181
|
+
# @see http://bit.ly/htCzCX AMQP 0.9.1 protocol documentation (Section 1.5.2.3.)
|
182
|
+
# @api public
|
183
|
+
def flow(active = false, &block)
|
184
|
+
@connection.send_frame(Protocol::Channel::Flow.encode(@id, active))
|
185
|
+
|
186
|
+
self.redefine_callback :flow, &block
|
187
|
+
self
|
188
|
+
end # flow(active = false, &block)
|
189
|
+
|
190
|
+
# @return [Boolean] True if flow in this channel is active (messages will be delivered to consumers that use this channel).
|
191
|
+
#
|
192
|
+
# @api public
|
193
|
+
def flow_is_active?
|
194
|
+
@flow_is_active
|
195
|
+
end # flow_is_active?
|
196
|
+
|
197
|
+
# @endgroup
|
198
|
+
|
199
|
+
|
200
|
+
|
201
|
+
# @group Transactions
|
202
|
+
|
203
|
+
# Sets the channel to use standard transactions. One must use this method at least
|
204
|
+
# once on a channel before using #tx_tommit or tx_rollback methods.
|
205
|
+
#
|
206
|
+
# @api public
|
207
|
+
def tx_select(&block)
|
208
|
+
@connection.send_frame(Protocol::Tx::Select.encode(@id))
|
209
|
+
|
210
|
+
self.redefine_callback :tx_select, &block
|
211
|
+
self
|
212
|
+
end # tx_select(&block)
|
213
|
+
|
214
|
+
# Commits AMQP transaction.
|
215
|
+
#
|
216
|
+
# @api public
|
217
|
+
def tx_commit(&block)
|
218
|
+
@connection.send_frame(Protocol::Tx::Commit.encode(@id))
|
219
|
+
|
220
|
+
self.redefine_callback :tx_commit, &block
|
221
|
+
self
|
222
|
+
end # tx_commit(&block)
|
223
|
+
|
224
|
+
# Rolls AMQP transaction back.
|
225
|
+
#
|
226
|
+
# @api public
|
227
|
+
def tx_rollback(&block)
|
228
|
+
@connection.send_frame(Protocol::Tx::Rollback.encode(@id))
|
229
|
+
|
230
|
+
self.redefine_callback :tx_rollback, &block
|
231
|
+
self
|
232
|
+
end # tx_rollback(&block)
|
233
|
+
|
234
|
+
# @endgroup
|
235
|
+
|
236
|
+
|
237
|
+
|
238
|
+
# @group Error handling
|
239
|
+
|
240
|
+
# Defines a callback that will be executed when channel is closed after
|
241
|
+
# channel-level exception.
|
242
|
+
#
|
243
|
+
# @api public
|
244
|
+
def on_error(&block)
|
245
|
+
self.define_callback(:error, &block)
|
246
|
+
end
|
247
|
+
|
248
|
+
# @endgroup
|
249
|
+
|
250
|
+
|
251
|
+
#
|
252
|
+
# Implementation
|
253
|
+
#
|
254
|
+
|
255
|
+
def register_exchange(exchange)
|
256
|
+
raise ArgumentError, "argument is nil!" if exchange.nil?
|
257
|
+
|
258
|
+
@exchanges[exchange.name] = exchange
|
259
|
+
end # register_exchange(exchange)
|
260
|
+
|
261
|
+
# Finds exchange in the exchanges cache on this channel by name. Exchange only exists in the cache if
|
262
|
+
# it was previously instantiated on this channel.
|
263
|
+
#
|
264
|
+
# @param [String] name Exchange name
|
265
|
+
# @return [AMQ::Client::Exchange] Exchange (if found)
|
266
|
+
# @api plugin
|
267
|
+
def find_exchange(name)
|
268
|
+
@exchanges[name]
|
269
|
+
end
|
270
|
+
|
271
|
+
def register_queue(queue)
|
272
|
+
raise ArgumentError, "argument is nil!" if queue.nil?
|
273
|
+
|
274
|
+
@queues[queue.name] = queue
|
275
|
+
end # register_queue(queue)
|
276
|
+
|
277
|
+
def find_queue(name)
|
278
|
+
@queues[name]
|
279
|
+
end
|
280
|
+
|
281
|
+
|
282
|
+
def reset_state!
|
283
|
+
@flow_is_active = true
|
284
|
+
|
285
|
+
@queues_awaiting_declare_ok = Array.new
|
286
|
+
@exchanges_awaiting_declare_ok = Array.new
|
287
|
+
|
288
|
+
@queues_awaiting_delete_ok = Array.new
|
289
|
+
|
290
|
+
@exchanges_awaiting_delete_ok = Array.new
|
291
|
+
@queues_awaiting_purge_ok = Array.new
|
292
|
+
@queues_awaiting_bind_ok = Array.new
|
293
|
+
@queues_awaiting_unbind_ok = Array.new
|
294
|
+
@queues_awaiting_consume_ok = Array.new
|
295
|
+
@queues_awaiting_cancel_ok = Array.new
|
296
|
+
|
297
|
+
@queues_awaiting_get_response = Array.new
|
298
|
+
|
299
|
+
@callbacks = Hash.new
|
300
|
+
end # reset_state!
|
301
|
+
|
302
|
+
|
303
|
+
def handle_connection_interruption(method = nil)
|
304
|
+
self.reset_state!
|
305
|
+
end # handle_connection_interruption
|
306
|
+
|
307
|
+
|
308
|
+
|
309
|
+
def handle_open_ok(open_ok)
|
310
|
+
self.status = :opened
|
311
|
+
self.exec_callback_once_yielding_self(:open, open_ok)
|
312
|
+
end
|
313
|
+
|
314
|
+
def handle_close_ok(close_ok)
|
315
|
+
self.status = :closed
|
316
|
+
self.exec_callback_once_yielding_self(:close, close_ok)
|
317
|
+
end
|
318
|
+
|
319
|
+
def handle_close(channel_close)
|
320
|
+
self.status = :closed
|
321
|
+
self.exec_callback_yielding_self(:error, channel_close)
|
322
|
+
|
323
|
+
self.handle_connection_interruption(channel_close)
|
324
|
+
end
|
325
|
+
|
326
|
+
|
327
|
+
|
328
|
+
self.handle(Protocol::Channel::OpenOk) do |connection, frame|
|
329
|
+
channel = connection.channels[frame.channel]
|
330
|
+
channel.handle_open_ok(frame.decode_payload)
|
331
|
+
end
|
332
|
+
|
333
|
+
self.handle(Protocol::Channel::CloseOk) do |connection, frame|
|
334
|
+
method = frame.decode_payload
|
335
|
+
channels = connection.channels
|
336
|
+
|
337
|
+
channel = channels[frame.channel]
|
338
|
+
channels.delete(channel)
|
339
|
+
channel.handle_close_ok(method)
|
340
|
+
end
|
341
|
+
|
342
|
+
self.handle(Protocol::Channel::Close) do |connection, frame|
|
343
|
+
method = frame.decode_payload
|
344
|
+
channels = connection.channels
|
345
|
+
channel = channels[frame.channel]
|
346
|
+
|
347
|
+
channel.handle_close(method)
|
348
|
+
end
|
349
|
+
|
350
|
+
self.handle(Protocol::Basic::QosOk) do |connection, frame|
|
351
|
+
channel = connection.channels[frame.channel]
|
352
|
+
channel.exec_callback(:qos, frame.decode_payload)
|
353
|
+
end
|
354
|
+
|
355
|
+
self.handle(Protocol::Basic::RecoverOk) do |connection, frame|
|
356
|
+
channel = connection.channels[frame.channel]
|
357
|
+
channel.exec_callback(:recover, frame.decode_payload)
|
358
|
+
end
|
359
|
+
|
360
|
+
self.handle(Protocol::Channel::FlowOk) do |connection, frame|
|
361
|
+
channel = connection.channels[frame.channel]
|
362
|
+
method = frame.decode_payload
|
363
|
+
|
364
|
+
channel.flow_is_active = method.active
|
365
|
+
channel.exec_callback(:flow, method)
|
366
|
+
end
|
367
|
+
|
368
|
+
self.handle(Protocol::Tx::SelectOk) do |connection, frame|
|
369
|
+
channel = connection.channels[frame.channel]
|
370
|
+
channel.exec_callback(:tx_select, frame.decode_payload)
|
371
|
+
end
|
372
|
+
|
373
|
+
self.handle(Protocol::Tx::CommitOk) do |connection, frame|
|
374
|
+
channel = connection.channels[frame.channel]
|
375
|
+
channel.exec_callback(:tx_commit, frame.decode_payload)
|
376
|
+
end
|
377
|
+
|
378
|
+
self.handle(Protocol::Tx::RollbackOk) do |connection, frame|
|
379
|
+
channel = connection.channels[frame.channel]
|
380
|
+
channel.exec_callback(:tx_rollback, frame.decode_payload)
|
381
|
+
end
|
382
|
+
end # Channel
|
383
|
+
end # Async
|
384
|
+
end # Client
|
385
|
+
end # AMQ
|