amq-client 0.7.0.alpha34 → 0.7.0.alpha35

Sign up to get free protection for your applications and to get access to all the features.
Files changed (32) hide show
  1. data/.travis.yml +4 -0
  2. data/Gemfile +1 -1
  3. data/README.textile +1 -1
  4. data/bin/ci/before_build.sh +24 -0
  5. data/examples/eventmachine_adapter/extensions/rabbitmq/handling_confirm_select_ok.rb +2 -2
  6. data/examples/eventmachine_adapter/extensions/rabbitmq/publisher_confirmations_with_transient_messages.rb +1 -1
  7. data/examples/eventmachine_adapter/extensions/rabbitmq/publisher_confirmations_with_unroutable_message.rb +1 -1
  8. data/lib/amq/client.rb +29 -17
  9. data/lib/amq/client/adapter.rb +8 -504
  10. data/lib/amq/client/adapters/coolio.rb +4 -282
  11. data/lib/amq/client/adapters/event_machine.rb +4 -382
  12. data/lib/amq/client/async/adapter.rb +517 -0
  13. data/lib/amq/client/async/adapters/coolio.rb +291 -0
  14. data/lib/amq/client/async/adapters/event_machine.rb +392 -0
  15. data/lib/amq/client/async/adapters/eventmachine.rb +1 -0
  16. data/lib/amq/client/async/callbacks.rb +71 -0
  17. data/lib/amq/client/async/channel.rb +385 -0
  18. data/lib/amq/client/async/entity.rb +66 -0
  19. data/lib/amq/client/async/exchange.rb +157 -0
  20. data/lib/amq/client/async/extensions/rabbitmq/basic.rb +38 -0
  21. data/lib/amq/client/async/extensions/rabbitmq/confirm.rb +248 -0
  22. data/lib/amq/client/async/queue.rb +455 -0
  23. data/lib/amq/client/callbacks.rb +6 -65
  24. data/lib/amq/client/channel.rb +4 -376
  25. data/lib/amq/client/entity.rb +6 -57
  26. data/lib/amq/client/exchange.rb +4 -148
  27. data/lib/amq/client/extensions/rabbitmq/basic.rb +4 -28
  28. data/lib/amq/client/extensions/rabbitmq/confirm.rb +5 -240
  29. data/lib/amq/client/queue.rb +5 -450
  30. data/lib/amq/client/version.rb +1 -1
  31. data/spec/unit/client_spec.rb +10 -30
  32. metadata +16 -22
@@ -1,64 +1,13 @@
1
1
  # encoding: utf-8
2
2
 
3
- require "amq/client/callbacks"
4
- require "amq/client/openable"
3
+ require "amq/client/async/entity"
5
4
 
6
5
  module AMQ
7
6
  module Client
8
- module RegisterEntityMixin
9
- # @example Registering Channel implementation
10
- # Adapter.register_entity(:channel, Channel)
11
- # # ... so then I can do:
12
- # channel = client.channel(1)
13
- # # instead of:
14
- # channel = Channel.new(client, 1)
15
- def register_entity(name, klass)
16
- define_method(name) do |*args, &block|
17
- klass.new(self, *args, &block)
18
- end # define_method
19
- end # register_entity
20
- end # RegisterEntityMixin
21
-
22
- module ProtocolMethodHandlers
23
- def handle(klass, &block)
24
- AMQ::Client::HandlersRegistry.register(klass, &block)
25
- end
26
-
27
- def handlers
28
- AMQ::Client::HandlersRegistry.handlers
29
- end
30
- end # ProtocolMethodHandlers
31
-
32
-
33
- # AMQ entities, as implemented by AMQ::Client, have callbacks and can run them
34
- # when necessary.
35
- #
36
- # @note Exchanges and queues implementation is based on this class.
37
- #
38
- # @abstract
39
- module Entity
40
-
41
- #
42
- # Behaviors
43
- #
44
-
45
- include Openable
46
- include Callbacks
47
-
48
- #
49
- # API
50
- #
51
-
52
- # @return [Array<#call>]
53
- attr_reader :callbacks
54
-
55
-
56
- def initialize(connection)
57
- @connection = connection
58
- # Be careful with default values for #ruby hashes: h = Hash.new(Array.new); h[:key] ||= 1
59
- # won't assign anything to :key. MK.
60
- @callbacks = Hash.new
61
- end # initialize
62
- end # Entity
7
+ # backwards compatibility
8
+ # @private
9
+ RegisterEntityMixin = Async::RegisterEntityMixin
10
+ ProtocolMethodHandlers = Async::ProtocolMethodHandlers
11
+ Entity = Async::Entity
63
12
  end # Client
64
13
  end # AMQ
@@ -1,155 +1,11 @@
1
1
  # encoding: utf-8
2
2
 
3
- require "amq/client/entity"
4
- require "amq/client/server_named_entity"
3
+ require "amq/client/async/exchange"
5
4
 
6
5
  module AMQ
7
6
  module Client
8
- class Exchange
9
-
10
-
11
- include Entity
12
- include ServerNamedEntity
13
- extend ProtocolMethodHandlers
14
-
15
- TYPES = [:fanout, :direct, :topic, :headers].freeze
16
-
17
- class IncompatibleExchangeTypeError < StandardError
18
- def initialize(types, given)
19
- super("#{given.inspect} exchange type is unknown. Standard types are #{TYPES.inspect}, custom exchange types must begin with x-, for example: x-recent-history")
20
- end
21
- end
22
-
23
-
24
- #
25
- # API
26
- #
27
-
28
- # Channel this exchange belongs to.
29
- attr_reader :channel
30
-
31
- # Exchange name. May be server-generated or assigned directly.
32
- attr_reader :name
33
-
34
- # @return [Symbol] One of :direct, :fanout, :topic, :headers
35
- attr_reader :type
36
-
37
- def initialize(connection, channel, name, type = :fanout)
38
- if !(TYPES.include?(type.to_sym) || type.to_s =~ /^x-.+/i)
39
- raise IncompatibleExchangeTypeError.new(TYPES, type)
40
- end
41
-
42
- @connection = connection
43
- @channel = channel
44
- @name = name
45
- @type = type
46
-
47
- # register pre-declared exchanges
48
- if @name == AMQ::Protocol::EMPTY_STRING || @name =~ /^amq\.(fanout|topic)/
49
- @channel.register_exchange(self)
50
- end
51
-
52
- super(connection)
53
- end
54
-
55
-
56
- def fanout?
57
- @type == :fanout
58
- end
59
-
60
- def direct?
61
- @type == :direct
62
- end
63
-
64
- def topic?
65
- @type == :topic
66
- end
67
-
68
-
69
-
70
- def declare(passive = false, durable = false, auto_delete = false, nowait = false, arguments = nil, &block)
71
- @connection.send_frame(Protocol::Exchange::Declare.encode(@channel.id, @name, @type.to_s, passive, durable, auto_delete, false, nowait, arguments))
72
-
73
- unless nowait
74
- self.define_callback(:declare, &block)
75
- @channel.exchanges_awaiting_declare_ok.push(self)
76
- end
77
-
78
- self
79
- end
80
-
81
-
82
- def delete(if_unused = false, nowait = false, &block)
83
- @connection.send_frame(Protocol::Exchange::Delete.encode(@channel.id, @name, if_unused, nowait))
84
-
85
- unless nowait
86
- self.define_callback(:delete, &block)
87
-
88
- # TODO: delete itself from exchanges cache
89
- @channel.exchanges_awaiting_delete_ok.push(self)
90
- end
91
-
92
- self
93
- end # delete(if_unused = false, nowait = false)
94
-
95
-
96
- def publish(payload, routing_key = AMQ::Protocol::EMPTY_STRING, user_headers = {}, mandatory = false, immediate = false, frame_size = nil)
97
- headers = { :priority => 0, :delivery_mode => 2, :content_type => "application/octet-stream" }.merge(user_headers)
98
- @connection.send_frameset(Protocol::Basic::Publish.encode(@channel.id, payload, headers, @name, routing_key, mandatory, immediate, (frame_size || @connection.frame_max)))
99
-
100
- self
101
- end
102
-
103
-
104
- def on_return(&block)
105
- self.redefine_callback(:return, &block)
106
-
107
- self
108
- end # on_return(&block)
109
-
110
-
111
-
112
-
113
- def handle_declare_ok(method)
114
- @name = method.exchange if self.anonymous?
115
- @channel.register_exchange(self)
116
-
117
- self.exec_callback_once_yielding_self(:declare, method)
118
- end
119
-
120
- def handle_delete_ok(method)
121
- self.exec_callback_once(:delete, method)
122
- end # handle_delete_ok(method)
123
-
124
-
125
-
126
- self.handle(Protocol::Exchange::DeclareOk) do |connection, frame|
127
- method = frame.decode_payload
128
- channel = connection.channels[frame.channel]
129
- exchange = channel.exchanges_awaiting_declare_ok.shift
130
-
131
- exchange.handle_declare_ok(method)
132
- end # handle
133
-
134
-
135
- self.handle(Protocol::Exchange::DeleteOk) do |connection, frame|
136
- channel = connection.channels[frame.channel]
137
- exchange = channel.exchanges_awaiting_delete_ok.shift
138
- exchange.handle_delete_ok(frame.decode_payload)
139
- end # handle
140
-
141
-
142
- self.handle(Protocol::Basic::Return) do |connection, frame, content_frames|
143
- channel = connection.channels[frame.channel]
144
- method = frame.decode_payload
145
- exchange = channel.find_exchange(method.exchange)
146
-
147
- header = content_frames.shift
148
- body = content_frames.map { |frame| frame.payload }.join
149
-
150
- exchange.exec_callback(:return, method, header, body)
151
- end
152
-
153
- end # Exchange
7
+ # backwards compatibility
8
+ # @private
9
+ Exchange = Async::Exchange
154
10
  end # Client
155
11
  end # AMQ
@@ -1,36 +1,12 @@
1
1
  # encoding: utf-8
2
2
 
3
- require "amq/client/channel"
3
+ require "amq/client/async/extensions/rabbitmq/basic"
4
4
 
5
5
  # Basic.Nack
6
6
  module AMQ
7
7
  module Client
8
- module Extensions
9
- module RabbitMQ
10
- module Basic
11
- module ChannelMixin
12
-
13
- # Overrides {AMQ::Client::Channel#reject} behavior to use basic.nack.
14
- #
15
- # @api public
16
- # @see http://www.rabbitmq.com/amqp-0-9-1-quickref.html#basic.nack
17
- def reject(delivery_tag, requeue = true, multi = false)
18
- if multi
19
- @connection.send_frame(Protocol::Basic::Nack.encode(self.id, delivery_tag, multi, requeue))
20
- else
21
- super(delivery_tag, requeue)
22
- end
23
- end # reject
24
-
25
- end # ChannelMixin
26
- end # Basic
27
- end # RabbitMQ
28
- end # Extensions
29
-
30
- class Channel
31
- # use modules, the native Ruby way of extension of existing classes,
32
- # instead of reckless monkey-patching. MK.
33
- include Extensions::RabbitMQ::Basic::ChannelMixin
34
- end
8
+ # backwards compatibility
9
+ # @private
10
+ Extensions = Async::Extensions
35
11
  end # Client
36
12
  end # AMQ
@@ -1,246 +1,11 @@
1
1
  # encoding: utf-8
2
2
 
3
+ require "amq/client/async/extensions/rabbitmq/confirm"
4
+
3
5
  module AMQ
4
6
  module Client
5
- module Extensions
6
- module RabbitMQ
7
- # h2. Purpose
8
- # In case that the broker crashes, some messages can get lost.
9
- # Thanks to this extension, broker sends Basic.Ack when the message
10
- # is processed by the broker. In case of persistent messages, it must
11
- # be written to disk or ack'd on all the queues it was delivered to.
12
- # However it doesn't have to be necessarily 1:1, because the broker
13
- # can send Basic.Ack with multi flag to acknowledge multiple messages.
14
- #
15
- # So it provides clients a lightweight way of keeping track of which
16
- # messages have been processed by the broker and which would need
17
- # re-publishing in case of broker shutdown or network failure.
18
- #
19
- # Transactions are solving the same problem, but they are very slow:
20
- # confirmations are more than 100 times faster.
21
- #
22
- # h2. Workflow
23
- # * Client asks broker to confirm messages on given channel (Confirm.Select).
24
- # * Broker sends back Confirm.Select-Ok, unless we sent Confirm.Select with nowait=true.
25
- # * After each published message, the client receives Basic.Ack from the broker.
26
- # * If something bad happens inside the broker, it sends Basic.Nack.
27
- #
28
- # h2. Gotchas
29
- # Note that we don't keep track of messages awaiting confirmation.
30
- # It'd add a huge overhead and it's impossible to come up with one-suits-all solution.
31
- # If you want to create such module, you'll probably want to redefine Channel#after_publish,
32
- # so it will put messages into a queue and then handlers for Basic.Ack and Basic.Nack.
33
- # This is the reason why we pass every argument from Exchange#publish to Channel#after_publish.
34
- # You should not forget though, that both of these methods can have multi flag!
35
- #
36
- # Transactional channel cannot be put into confirm mode and a confirm
37
- # mode channel cannot be made transactional.
38
- #
39
- # If the connection between the publisher and broker drops with outstanding
40
- # confirms, it does not necessarily mean that the messages were lost, so
41
- # republishing may result in duplicate messages.
42
-
43
- # h2. Learn more
44
- # @see http://www.rabbitmq.com/blog/2011/02/10/introducing-publisher-confirms
45
- # @see http://www.rabbitmq.com/amqp-0-9-1-quickref.html#class.confirm
46
- # @see http://www.rabbitmq.com/amqp-0-9-1-reference.html#basic.ack
47
- module Confirm
48
- module ChannelMixin
49
-
50
- # Change publisher index. Publisher index is incremented
51
- # by 1 after each Basic.Publish starting at 1. This is done
52
- # on both client and server, hence this acknowledged messages
53
- # can be matched via its delivery-tag.
54
- #
55
- # @api private
56
- attr_writer :publisher_index
57
-
58
- # Publisher index is an index of the last message since
59
- # the confirmations were activated, started with 1. It's
60
- # incremented by 1 after each Basic.Publish starting at 1.
61
- # This is done on both client and server, hence this
62
- # acknowledged messages can be matched via its delivery-tag.
63
- #
64
- # @return [Integer] Current publisher index.
65
- # @api public
66
- def publisher_index
67
- @publisher_index ||= 1
68
- end
69
-
70
- # Resets publisher index to 0
71
- #
72
- # @api plugin
73
- def reset_publisher_index!
74
- @publisher_index = 0
75
- end
76
-
77
-
78
- # This method is executed after publishing of each message via {Exchage#publish}.
79
- # Currently it just increments publisher index by 1, so messages
80
- # can be actually matched.
81
- #
82
- # @api plugin
83
- def after_publish(*args)
84
- self.publisher_index += 1
85
- end
86
-
87
- # Turn on confirmations for this channel and, if given,
88
- # register callback for Confirm.Select-Ok.
89
- #
90
- # @raise [RuntimeError] Occurs when confirmations are already activated.
91
- # @raise [RuntimeError] Occurs when nowait is true and block is given.
92
- #
93
- # @param [Boolean] nowait Whether we expect Confirm.Select-Ok to be returned by the broker or not.
94
- # @yield [method] Callback which will be executed once we receive Confirm.Select-Ok.
95
- # @yieldparam [AMQ::Protocol::Confirm::SelectOk] method Protocol method class instance.
96
- #
97
- # @return [self] self.
98
- #
99
- # @see #confirm
100
- def confirm_select(nowait = false, &block)
101
- if nowait && block
102
- raise "You can't use Confirm.Select with nowait=true and a callback at the same time."
103
- end
104
-
105
- @uses_publisher_confirmations = true
106
- self.redefine_callback(:confirm_select, &block)
107
- @connection.send_frame(Protocol::Confirm::Select.encode(@id, nowait))
108
-
109
- self
110
- end
111
-
112
- # @return [Boolean]
113
- def uses_publisher_confirmations?
114
- @uses_publisher_confirmations
115
- end # uses_publisher_confirmations?
116
-
117
-
118
- # Turn on confirmations for this channel and, if given,
119
- # register callback for basic.ack from the broker.
120
- #
121
- # @raise [RuntimeError] Occurs when confirmations are already activated.
122
- # @raise [RuntimeError] Occurs when nowait is true and block is given.
123
- # @param [Boolean] nowait Whether we expect Confirm.Select-Ok to be returned by the broker or not.
124
- #
125
- # @yield [basick_ack] Callback which will be executed every time we receive Basic.Ack from the broker.
126
- # @yieldparam [AMQ::Protocol::Basic::Ack] basick_ack Protocol method class instance.
127
- #
128
- # @return [self] self.
129
- def on_ack(nowait = false, &block)
130
- self.use_publisher_confirmations! unless self.uses_publisher_confirmations?
131
-
132
- self.define_callback(:ack, &block) if block
133
-
134
- self
135
- end
136
-
137
-
138
- # Register error callback for Basic.Nack. It's called
139
- # when message(s) is rejected.
140
- #
141
- # @return [self] self
142
- def on_nack(&block)
143
- self.define_callback(:nack, &block) if block
144
-
145
- self
146
- end
147
-
148
-
149
-
150
-
151
- # Handler for Confirm.Select-Ok. By default, it just
152
- # executes hook specified via the #confirmations method
153
- # with a single argument, a protocol method class
154
- # instance (an instance of AMQ::Protocol::Confirm::SelectOk)
155
- # and then it deletes the callback, since Confirm.Select
156
- # is supposed to be sent just once.
157
- #
158
- # @api plugin
159
- def handle_select_ok(method)
160
- self.exec_callback_once(:confirm_select, method)
161
- end
162
-
163
- # Handler for Basic.Ack. By default, it just
164
- # executes hook specified via the #confirm method
165
- # with a single argument, a protocol method class
166
- # instance (an instance of AMQ::Protocol::Basic::Ack).
167
- #
168
- # @api plugin
169
- def handle_basic_ack(method)
170
- self.exec_callback(:ack, method)
171
- end
172
-
173
-
174
- # Handler for Basic.Nack. By default, it just
175
- # executes hook specified via the #confirm_failed method
176
- # with a single argument, a protocol method class
177
- # instance (an instance of AMQ::Protocol::Basic::Nack).
178
- #
179
- # @api plugin
180
- def handle_basic_nack(method)
181
- self.exec_callback(:nack, method)
182
- end
183
-
184
-
185
- def reset_state!
186
- super
187
-
188
- @uses_publisher_confirmations = false
189
- end
190
-
191
-
192
- def self.included(host)
193
- host.handle(Protocol::Confirm::SelectOk) do |connection, frame|
194
- method = frame.decode_payload
195
- channel = connection.channels[frame.channel]
196
- channel.handle_select_ok(method)
197
- end
198
-
199
- host.handle(Protocol::Basic::Ack) do |connection, frame|
200
- method = frame.decode_payload
201
- channel = connection.channels[frame.channel]
202
- channel.handle_basic_ack(method)
203
- end
204
-
205
- host.handle(Protocol::Basic::Nack) do |connection, frame|
206
- method = frame.decode_payload
207
- channel = connection.channels[frame.channel]
208
- channel.handle_basic_nack(method)
209
- end
210
- end # self.included(host)
211
- end # ChannelMixin
212
-
213
-
214
- module ExchangeMixin
215
- # Publish message and then run #after_publish on channel belonging
216
- # to the exchange. This is used for incrementing the publisher index.
217
- #
218
- # @api public
219
- # @see AMQ::Client::Exchange#publish
220
- # @see AMQ::Client::Extensions::RabbitMQ::Channel#publisher_index
221
- # @return [self] self
222
- def publish(*args)
223
- super(*args)
224
- @channel.after_publish(*args)
225
-
226
- self
227
- end # publish
228
- end # ExchangeMixin
229
- end # Confirm
230
- end # RabbitMQ
231
- end # Extensions
232
-
233
-
234
- class Channel
235
- # use modules, a native Ruby way of extension of existing classes,
236
- # instead of reckless monkey-patching. MK.
237
- include Extensions::RabbitMQ::Confirm::ChannelMixin
238
- end # Channel
239
-
240
- class Exchange
241
- # use modules, a native Ruby way of extension of existing classes,
242
- # instead of reckless monkey-patching. MK.
243
- include Extensions::RabbitMQ::Confirm::ExchangeMixin
244
- end # Exchange
7
+ # backwards compatibility
8
+ # @private
9
+ Extensions = Async::Extensions
245
10
  end # Client
246
11
  end # AMQ