hot_bunnies 2.0.0.pre8-java → 2.0.0.pre9-java
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/lib/hot_bunnies.rb +7 -0
- data/lib/hot_bunnies/channel.rb +425 -1
- data/lib/hot_bunnies/consumers.rb +4 -208
- data/lib/hot_bunnies/consumers/base.rb +72 -0
- data/lib/hot_bunnies/consumers/blocking.rb +82 -0
- data/lib/hot_bunnies/consumers/non_blocking.rb +73 -0
- data/lib/hot_bunnies/session.rb +56 -3
- data/lib/hot_bunnies/thread_pools.rb +32 -0
- data/lib/hot_bunnies/version.rb +1 -1
- metadata +6 -2
data/lib/hot_bunnies.rb
CHANGED
@@ -6,10 +6,17 @@ require 'ext/rabbitmq-client'
|
|
6
6
|
|
7
7
|
require 'hot_bunnies/version'
|
8
8
|
require 'hot_bunnies/exceptions'
|
9
|
+
require 'hot_bunnies/thread_pools'
|
9
10
|
require 'hot_bunnies/session'
|
10
11
|
|
12
|
+
# HotBunnies is a JRuby client for RabbitMQ built on top of the official Java client.
|
13
|
+
#
|
14
|
+
# @see HotBunnies.connect
|
15
|
+
# @see HotBunnies::Session
|
16
|
+
# @see HotBunnies::Channel
|
11
17
|
module HotBunnies
|
12
18
|
# Delegates to {HotBunnies::Session.connect}
|
19
|
+
# @see HotBunnies::Session.connect
|
13
20
|
def self.connect(*args)
|
14
21
|
Session.connect(*args)
|
15
22
|
end
|
data/lib/hot_bunnies/channel.rb
CHANGED
@@ -2,9 +2,121 @@
|
|
2
2
|
require "hot_bunnies/shutdown_listener"
|
3
3
|
|
4
4
|
module HotBunnies
|
5
|
+
# ## Channels in RabbitMQ
|
6
|
+
#
|
7
|
+
# To quote {http://www.rabbitmq.com/resources/specs/amqp0-9-1.pdf AMQP 0.9.1 specification}:
|
8
|
+
#
|
9
|
+
# AMQP 0.9.1 is a multi-channelled protocol. Channels provide a way to multiplex
|
10
|
+
# a heavyweight TCP/IP connection into several light weight connections.
|
11
|
+
# This makes the protocol more “firewall friendly” since port usage is predictable.
|
12
|
+
# It also means that traffic shaping and other network QoS features can be easily employed.
|
13
|
+
# Channels are independent of each other and can perform different functions simultaneously
|
14
|
+
# with other channels, the available bandwidth being shared between the concurrent activities.
|
15
|
+
#
|
16
|
+
#
|
17
|
+
# ## Opening Channels
|
18
|
+
#
|
19
|
+
# Channels can be opened either via `HotBunnies::Session#create_channel` (sufficient in the majority
|
20
|
+
# of cases) or by instantiating `HotBunnies::Channel` directly:
|
21
|
+
#
|
22
|
+
# @example Using {HotBunnies::Session#create_channel}:
|
23
|
+
# conn = HotBunnies.new
|
24
|
+
# conn.start
|
25
|
+
#
|
26
|
+
# ch = conn.create_channel
|
27
|
+
#
|
28
|
+
# This will automatically allocate a channel id.
|
29
|
+
#
|
30
|
+
# ## Closing Channels
|
31
|
+
#
|
32
|
+
# Channels are closed via {HotBunnies::Channel#close}. Channels that get a channel-level exception are
|
33
|
+
# closed, too. Closed channels can no longer be used. Attempts to use them will raise
|
34
|
+
# {HotBunnies::ChannelAlreadyClosed}.
|
35
|
+
#
|
36
|
+
# @example
|
37
|
+
#
|
38
|
+
# ch = conn.create_channel
|
39
|
+
# ch.close
|
40
|
+
#
|
41
|
+
# ## Higher-level API
|
42
|
+
#
|
43
|
+
# HotBunnies offers two sets of methods on {HotBunnies::Channel}: known as higher-level and lower-level
|
44
|
+
# APIs, respectively. Higher-level API mimics {http://rubyamqp.info amqp gem} API where
|
45
|
+
# exchanges and queues are objects (instance of {HotBunnies::Exchange} and {HotBunnies::Queue}, respectively).
|
46
|
+
# Lower-level API is built around AMQP 0.9.1 methods (commands), where queues and exchanges are
|
47
|
+
# passed as strings (à la RabbitMQ Java client, {http://clojurerabbitmq.info Langohr} and Pika).
|
48
|
+
#
|
49
|
+
# ### Queue Operations In Higher-level API
|
50
|
+
#
|
51
|
+
# * {HotBunnies::Channel#queue} is used to declare queues. The rest of the API is in {HotBunnies::Queue}.
|
52
|
+
#
|
53
|
+
#
|
54
|
+
# ### Exchange Operations In Higher-level API
|
55
|
+
#
|
56
|
+
# * {HotBunnies::Channel#topic} declares a topic exchange. The rest of the API is in {HotBunnies::Exchange}.
|
57
|
+
# * {HotBunnies::Channel#direct} declares a direct exchange.
|
58
|
+
# * {HotBunnies::Channel#fanout} declares a fanout exchange.
|
59
|
+
# * {HotBunnies::Channel#headers} declares a headers exchange.
|
60
|
+
# * {HotBunnies::Channel#default_exchange}
|
61
|
+
# * {HotBunnies::Channel#exchange} is used to declare exchanges with type specified as a symbol or string.
|
62
|
+
#
|
63
|
+
#
|
64
|
+
# ## Channel Qos (Prefetch Level)
|
65
|
+
#
|
66
|
+
# It is possible to control how many messages at most a consumer will be given (before it acknowledges
|
67
|
+
# or rejects previously consumed ones). This setting is per channel and controlled via {HotBunnies::Channel#prefetch}.
|
68
|
+
#
|
69
|
+
#
|
70
|
+
# ## Channel IDs
|
71
|
+
#
|
72
|
+
# Channels are identified by their ids which are integers. HotBunnies takes care of allocating and
|
73
|
+
# releasing them as channels are opened and closed. It is almost never necessary to specify
|
74
|
+
# channel ids explicitly.
|
75
|
+
#
|
76
|
+
# There is a limit on the maximum number of channels per connection, usually 65536. Note
|
77
|
+
# that allocating channels is very cheap on both client and server so having tens, hundreds
|
78
|
+
# or even thousands of channels is possible.
|
79
|
+
#
|
80
|
+
# ## Channels and Error Handling
|
81
|
+
#
|
82
|
+
# Channel-level exceptions are more common than connection-level ones and often indicate
|
83
|
+
# issues applications can recover from (such as consuming from or trying to delete
|
84
|
+
# a queue that does not exist).
|
85
|
+
#
|
86
|
+
# With HotBunnies, channel-level exceptions are raised as Ruby exceptions, for example,
|
87
|
+
# {HotBunnies::NotFound}, that provide access to the underlying `channel.close` method
|
88
|
+
# information.
|
89
|
+
#
|
90
|
+
# @example Handling 404 NOT_FOUND
|
91
|
+
# begin
|
92
|
+
# ch.queue_delete("queue_that_should_not_exist#{rand}")
|
93
|
+
# rescue HotBunnies::NotFound => e
|
94
|
+
# puts "Channel-level exception! Code: #{e.channel_close.reply_code}, message: #{e.channel_close.reply_text}"
|
95
|
+
# end
|
96
|
+
#
|
97
|
+
# @example Handling 406 PRECONDITION_FAILED
|
98
|
+
# begin
|
99
|
+
# ch2 = conn.create_channel
|
100
|
+
# q = "hotbunnies.examples.recovery.q#{rand}"
|
101
|
+
#
|
102
|
+
# ch2.queue_declare(q, :durable => false)
|
103
|
+
# ch2.queue_declare(q, :durable => true)
|
104
|
+
# rescue HotBunnies::PreconditionFailed => e
|
105
|
+
# puts "Channel-level exception! Code: #{e.channel_close.reply_code}, message: #{e.channel_close.reply_text}"
|
106
|
+
# ensure
|
107
|
+
# conn.create_channel.queue_delete(q)
|
108
|
+
# end
|
109
|
+
#
|
110
|
+
# @see HotBunnies::Session#create_channel
|
111
|
+
# @see http://www.rabbitmq.com/tutorials/amqp-concepts.html AMQP 0.9.1 Model Concepts Guide
|
112
|
+
# @see http://hotbunnies.info/articles/getting_started.html Getting Started with RabbitMQ Using HotBunnies
|
113
|
+
# @see http://hotbunnies.info/articles/queues.html Queues and Consumers
|
114
|
+
# @see http://hotbunnies.info/articles/exchanges.html Exchanges and Publishing
|
5
115
|
class Channel
|
6
|
-
|
116
|
+
# @return [Array<HotBunnies::Consumer>] Consumers on this channel
|
117
|
+
attr_reader :consumers
|
7
118
|
|
119
|
+
# @private
|
8
120
|
def initialize(session, delegate)
|
9
121
|
@connection = session
|
10
122
|
@delegate = delegate
|
@@ -19,26 +131,41 @@ module HotBunnies
|
|
19
131
|
end
|
20
132
|
end
|
21
133
|
|
134
|
+
# @return [HotBunnies::Session] Connection this channel is on
|
22
135
|
def client
|
23
136
|
@connection
|
24
137
|
end
|
25
138
|
|
139
|
+
# @return [HotBunnies::Session] Connection this channel is on
|
140
|
+
def session
|
141
|
+
@connection
|
142
|
+
end
|
143
|
+
|
144
|
+
# @return [HotBunnies::Session] Connection this channel is on
|
26
145
|
def connection
|
27
146
|
@connection
|
28
147
|
end
|
29
148
|
|
149
|
+
# @return [Integer] Channel id
|
30
150
|
def id
|
31
151
|
@delegate.channel_number
|
32
152
|
end
|
33
153
|
|
154
|
+
# @return [Integer] Channel id
|
34
155
|
def number
|
35
156
|
@delegate.channel_number
|
36
157
|
end
|
37
158
|
|
159
|
+
# @return [Integer] Channel id
|
38
160
|
def channel_number
|
39
161
|
@delegate.channel_number
|
40
162
|
end
|
41
163
|
|
164
|
+
# Closes the channel.
|
165
|
+
#
|
166
|
+
# Closed channels can no longer be used. Closed channel id is
|
167
|
+
# returned back to the pool of available ids and may be used by
|
168
|
+
# a different channel opened later.
|
42
169
|
def close(code = 200, reason = "Goodbye")
|
43
170
|
v = @delegate.close(code, reason)
|
44
171
|
|
@@ -60,40 +187,125 @@ module HotBunnies
|
|
60
187
|
|
61
188
|
# @group Exchanges
|
62
189
|
|
190
|
+
# Declares a headers exchange or looks it up in the cache of previously
|
191
|
+
# declared exchanges.
|
192
|
+
#
|
193
|
+
# @param [String] name Exchange name
|
194
|
+
# @param [Hash] opts Exchange parameters
|
195
|
+
#
|
196
|
+
# @option options [String,Symbol] :type (:direct) Exchange type, e.g. :fanout or "x-consistent-hash"
|
197
|
+
# @option options [Boolean] :durable (false) Should the exchange be durable?
|
198
|
+
# @option options [Boolean] :auto_delete (false) Should the exchange be automatically deleted when no longer in use?
|
199
|
+
# @option options [Hash] :arguments ({}) Optional exchange arguments
|
200
|
+
#
|
201
|
+
# @return [HotBunnies::Exchange] Exchange instance
|
202
|
+
# @see http://hotbunnies.info/articles/exchanges.html Exchanges and Publishing guide
|
203
|
+
# @see http://hotbunnies.info/articles/extensions.html RabbitMQ Extensions to AMQP 0.9.1 guide
|
63
204
|
def exchange(name, options={})
|
64
205
|
Exchange.new(self, name, options).tap do |x|
|
65
206
|
x.declare!
|
66
207
|
end
|
67
208
|
end
|
68
209
|
|
210
|
+
# Declares a fanout exchange or looks it up in the cache of previously
|
211
|
+
# declared exchanges.
|
212
|
+
#
|
213
|
+
# @param [String] name Exchange name
|
214
|
+
# @param [Hash] opts Exchange parameters
|
215
|
+
#
|
216
|
+
# @option opts [Boolean] :durable (false) Should the exchange be durable?
|
217
|
+
# @option opts [Boolean] :auto_delete (false) Should the exchange be automatically deleted when no longer in use?
|
218
|
+
# @option opts [Hash] :arguments ({}) Optional exchange arguments (used by RabbitMQ extensions)
|
219
|
+
#
|
220
|
+
# @return [HotBunnies::Exchange] Exchange instance
|
221
|
+
# @see http://hotbunnies.info/articles/exchanges.html Exchanges and Publishing guide
|
222
|
+
# @see http://hotbunnies.info/articles/extensions.html RabbitMQ Extensions to AMQP 0.9.1 guide
|
223
|
+
# @api public
|
69
224
|
def fanout(name, opts = {})
|
70
225
|
Exchange.new(self, name, opts.merge(:type => "fanout")).tap do |x|
|
71
226
|
x.declare!
|
72
227
|
end
|
73
228
|
end
|
74
229
|
|
230
|
+
# Declares a direct exchange or looks it up in the cache of previously
|
231
|
+
# declared exchanges.
|
232
|
+
#
|
233
|
+
# @param [String] name Exchange name
|
234
|
+
# @param [Hash] opts Exchange parameters
|
235
|
+
#
|
236
|
+
# @option opts [Boolean] :durable (false) Should the exchange be durable?
|
237
|
+
# @option opts [Boolean] :auto_delete (false) Should the exchange be automatically deleted when no longer in use?
|
238
|
+
# @option opts [Hash] :arguments ({}) Optional exchange arguments (used by RabbitMQ extensions)
|
239
|
+
#
|
240
|
+
# @return [HotBunnies::Exchange] Exchange instance
|
241
|
+
# @see http://hotbunnies.info/articles/exchanges.html Exchanges and Publishing guide
|
242
|
+
# @see http://hotbunnies.info/articles/extensions.html RabbitMQ Extensions to AMQP 0.9.1 guide
|
243
|
+
# @api public
|
75
244
|
def direct(name, opts = {})
|
76
245
|
Exchange.new(self, name, opts.merge(:type => "direct")).tap do |x|
|
77
246
|
x.declare!
|
78
247
|
end
|
79
248
|
end
|
80
249
|
|
250
|
+
# Declares a topic exchange or looks it up in the cache of previously
|
251
|
+
# declared exchanges.
|
252
|
+
#
|
253
|
+
# @param [String] name Exchange name
|
254
|
+
# @param [Hash] opts Exchange parameters
|
255
|
+
#
|
256
|
+
# @option opts [Boolean] :durable (false) Should the exchange be durable?
|
257
|
+
# @option opts [Boolean] :auto_delete (false) Should the exchange be automatically deleted when no longer in use?
|
258
|
+
# @option opts [Hash] :arguments ({}) Optional exchange arguments (used by RabbitMQ extensions)
|
259
|
+
#
|
260
|
+
# @return [HotBunnies::Exchange] Exchange instance
|
261
|
+
# @see http://hotbunnies.info/articles/exchanges.html Exchanges and Publishing guide
|
262
|
+
# @see http://hotbunnies.info/articles/extensions.html RabbitMQ Extensions to AMQP 0.9.1 guide
|
263
|
+
# @api public
|
81
264
|
def topic(name, opts = {})
|
82
265
|
Exchange.new(self, name, opts.merge(:type => "topic")).tap do |x|
|
83
266
|
x.declare!
|
84
267
|
end
|
85
268
|
end
|
86
269
|
|
270
|
+
# Declares a headers exchange or looks it up in the cache of previously
|
271
|
+
# declared exchanges.
|
272
|
+
#
|
273
|
+
# @param [String] name Exchange name
|
274
|
+
# @param [Hash] opts Exchange parameters
|
275
|
+
#
|
276
|
+
# @option opts [Boolean] :durable (false) Should the exchange be durable?
|
277
|
+
# @option opts [Boolean] :auto_delete (false) Should the exchange be automatically deleted when no longer in use?
|
278
|
+
# @option opts [Hash] :arguments ({}) Optional exchange arguments
|
279
|
+
#
|
280
|
+
# @return [HotBunnies::Exchange] Exchange instance
|
281
|
+
# @see http://hotbunnies.info/articles/exchanges.html Exchanges and Publishing guide
|
282
|
+
# @see http://hotbunnies.info/articles/extensions.html RabbitMQ Extensions to AMQP 0.9.1 guide
|
283
|
+
# @api public
|
87
284
|
def headers(name, opts = {})
|
88
285
|
Exchange.new(self, name, opts.merge(:type => "headers")).tap do |x|
|
89
286
|
x.declare!
|
90
287
|
end
|
91
288
|
end
|
92
289
|
|
290
|
+
# Provides access to the default exchange
|
291
|
+
# @see http://hotbunnies.info/articles/exchanges.html Exchanges and Publishing guide
|
292
|
+
# @api public
|
93
293
|
def default_exchange
|
94
294
|
@default_exchange ||= self.exchange("", :durable => true, :auto_delete => false, :type => "direct")
|
95
295
|
end
|
96
296
|
|
297
|
+
# Declares a echange using echange.declare AMQP 0.9.1 method.
|
298
|
+
#
|
299
|
+
# @param [String] name Exchange name
|
300
|
+
# @param [Boolean] durable (false) Should information about this echange be persisted to disk so that it
|
301
|
+
# can survive broker restarts? Typically set to true for long-lived exchanges.
|
302
|
+
# @param [Boolean] auto_delete (false) Should this echange be deleted when it is no longer used?
|
303
|
+
# @param [Boolean] passive (false) If true, exchange will be checked for existence. If it does not
|
304
|
+
# exist, {Bunny::NotFound} will be raised.
|
305
|
+
#
|
306
|
+
# @return RabbitMQ response
|
307
|
+
# @see http://hotbunnies.info/articles/echanges.html Exchanges and Publishing guide
|
308
|
+
# @api public
|
97
309
|
def exchange_declare(name, type, durable = false, auto_delete = false, arguments = nil)
|
98
310
|
@delegate.exchange_declare(name, type, durable, auto_delete, arguments)
|
99
311
|
end
|
@@ -103,42 +315,120 @@ module HotBunnies
|
|
103
315
|
|
104
316
|
# @group Queues
|
105
317
|
|
318
|
+
# Declares a queue or looks it up in the per-channel cache.
|
319
|
+
#
|
320
|
+
# @param [String] name Queue name. Pass an empty string to declare a server-named queue (make RabbitMQ generate a unique name).
|
321
|
+
# @param [Hash] options Queue properties and other options
|
322
|
+
#
|
323
|
+
# @option options [Boolean] :durable (false) Should this queue be durable?
|
324
|
+
# @option options [Boolean] :auto-delete (false) Should this queue be automatically deleted when the last consumer disconnects?
|
325
|
+
# @option options [Boolean] :exclusive (false) Should this queue be exclusive (only can be used by this connection, removed when the connection is closed)?
|
326
|
+
# @option options [Boolean] :arguments ({}) Additional optional arguments (typically used by RabbitMQ extensions and plugins)
|
327
|
+
#
|
328
|
+
# @return [HotBunnies::Queue] Queue that was declared or looked up in the cache
|
329
|
+
# @see http://hotbunnies.info/articles/queues.html Queues and Consumers guide
|
330
|
+
# @see http://hotbunnies.info/articles/extensions.html RabbitMQ Extensions guide
|
331
|
+
# @api public
|
106
332
|
def queue(name, options={})
|
107
333
|
Queue.new(self, name, options).tap do |q|
|
108
334
|
q.declare!
|
109
335
|
end
|
110
336
|
end
|
111
337
|
|
338
|
+
# Declares a queue using queue.declare AMQP 0.9.1 method.
|
339
|
+
#
|
340
|
+
# @param [String] name Queue name
|
341
|
+
#
|
342
|
+
# @param [Boolean] durable (false) Should information about this queue be persisted to disk so that it
|
343
|
+
# can survive broker restarts? Typically set to true for long-lived queues.
|
344
|
+
# @param [Boolean] auto_delete (false) Should this queue be deleted when the last consumer is cancelled?
|
345
|
+
# @param [Boolean] exclusive (false) Should only this connection be able to use this queue?
|
346
|
+
# If true, the queue will be automatically deleted when this
|
347
|
+
# connection is closed
|
348
|
+
# @param [Boolean] passive (false) If true, queue will be checked for existence. If it does not
|
349
|
+
# exist, {Bunny::NotFound} will be raised.
|
350
|
+
#
|
351
|
+
# @return RabbitMQ response
|
352
|
+
# @see http://hotbunnies.info/articles/queues.html Queues and Consumers guide
|
353
|
+
# @api public
|
112
354
|
def queue_declare(name, durable, exclusive, auto_delete, arguments = {})
|
113
355
|
converting_rjc_exceptions_to_ruby do
|
114
356
|
@delegate.queue_declare(name, durable, exclusive, auto_delete, arguments)
|
115
357
|
end
|
116
358
|
end
|
117
359
|
|
360
|
+
# Checks if a queue exists using queue.declare AMQP 0.9.1 method.
|
361
|
+
# If it does not, a channel exception will be raised.
|
362
|
+
#
|
363
|
+
# @param [String] name Queue name
|
364
|
+
#
|
365
|
+
# @see http://hotbunnies.info/articles/queues.html Queues and Consumers guide
|
366
|
+
# @api public
|
118
367
|
def queue_declare_passive(name)
|
119
368
|
converting_rjc_exceptions_to_ruby do
|
120
369
|
@delegate.queue_declare_passive(name)
|
121
370
|
end
|
122
371
|
end
|
123
372
|
|
373
|
+
# Deletes a queue using queue.delete AMQP 0.9.1 method
|
374
|
+
#
|
375
|
+
# @param [String] name Queue name
|
376
|
+
#
|
377
|
+
# @param [Boolean] if_empty (false) Should this queue be deleted only if it has no messages?
|
378
|
+
# @param [Boolean] if_unused (false) Should this queue be deleted only if it has no consumers?
|
379
|
+
#
|
380
|
+
# @return RabbitMQ response
|
381
|
+
# @see http://hotbunnies.info/articles/queues.html Queues and Consumers guide
|
382
|
+
# @api public
|
124
383
|
def queue_delete(name, if_empty = false, if_unused = false)
|
125
384
|
converting_rjc_exceptions_to_ruby do
|
126
385
|
@delegate.queue_delete(name, if_empty, if_unused)
|
127
386
|
end
|
128
387
|
end
|
129
388
|
|
389
|
+
# Binds a queue to an exchange using queue.bind AMQP 0.9.1 method
|
390
|
+
#
|
391
|
+
# @param [String] name Queue name
|
392
|
+
# @param [String] exchange Exchange name
|
393
|
+
#
|
394
|
+
# @param [String] routing_key Routing key used for binding
|
395
|
+
# @param [Hash] arguments (nil) Optional arguments
|
396
|
+
#
|
397
|
+
# @return RabbitMQ response
|
398
|
+
# @see http://hotbunnies.info/articles/queues.html Queues and Consumers guide
|
399
|
+
# @see http://hotbunnies.info/articles/bindings.html Bindings guide
|
400
|
+
# @api public
|
130
401
|
def queue_bind(queue, exchange, routing_key, arguments = nil)
|
131
402
|
converting_rjc_exceptions_to_ruby do
|
132
403
|
@delegate.queue_bind(queue, exchange, routing_key, arguments)
|
133
404
|
end
|
134
405
|
end
|
135
406
|
|
407
|
+
# Unbinds a queue from an exchange using queue.unbind AMQP 0.9.1 method
|
408
|
+
#
|
409
|
+
# @param [String] name Queue name
|
410
|
+
# @param [String] exchange Exchange name
|
411
|
+
#
|
412
|
+
# @param [String] routing_key Routing key used for binding
|
413
|
+
# @param [Hash] arguments ({}) Optional arguments
|
414
|
+
#
|
415
|
+
# @return RabbitMQ response
|
416
|
+
# @see http://hotbunnies.info/articles/queues.html Queues and Consumers guide
|
417
|
+
# @see http://hotbunnies.info/articles/bindings.html Bindings guide
|
418
|
+
# @api public
|
136
419
|
def queue_unbind(queue, exchange, routing_key, arguments = nil)
|
137
420
|
converting_rjc_exceptions_to_ruby do
|
138
421
|
@delegate.queue_unbind(queue, exchange, routing_key, arguments)
|
139
422
|
end
|
140
423
|
end
|
141
424
|
|
425
|
+
# Purges a queue (removes all messages from it) using queue.purge AMQP 0.9.1 method.
|
426
|
+
#
|
427
|
+
# @param [String] name Queue name
|
428
|
+
#
|
429
|
+
# @return RabbitMQ response
|
430
|
+
# @see http://hotbunnies.info/articles/queues.html Queues and Consumers guide
|
431
|
+
# @api public
|
142
432
|
def queue_purge(name)
|
143
433
|
converting_rjc_exceptions_to_ruby do
|
144
434
|
@delegate.queue_purge(name)
|
@@ -150,6 +440,30 @@ module HotBunnies
|
|
150
440
|
|
151
441
|
# @group basic.*
|
152
442
|
|
443
|
+
# Publishes a message using basic.publish AMQP 0.9.1 method.
|
444
|
+
#
|
445
|
+
# @param [String] exchange Exchange to publish to
|
446
|
+
# @param [String] routing_key Routing key
|
447
|
+
# @param [String] body Message payload. It will never be modified by Bunny or RabbitMQ in any way.
|
448
|
+
# @option opts [Boolean] :mandatory Should the message be returned if it cannot be routed to any queue?
|
449
|
+
#
|
450
|
+
# @param [Hash] properties Message properties
|
451
|
+
#
|
452
|
+
# @option properties [Boolean] :persistent Should the message be persisted to disk?
|
453
|
+
# @option properties [Integer] :timestamp A timestamp associated with this message
|
454
|
+
# @option properties [Integer] :expiration Expiration time after which the message will be deleted
|
455
|
+
# @option properties [String] :type Message type, e.g. what type of event or command this message represents. Can be any string
|
456
|
+
# @option properties [String] :reply_to Queue name other apps should send the response to
|
457
|
+
# @option properties [String] :content_type Message content type (e.g. application/json)
|
458
|
+
# @option properties [String] :content_encoding Message content encoding (e.g. gzip)
|
459
|
+
# @option properties [String] :correlation_id Message correlated to this one, e.g. what request this message is a reply for
|
460
|
+
# @option properties [Integer] :priority Message priority, 0 to 9. Not used by RabbitMQ, only applications
|
461
|
+
# @option properties [String] :message_id Any message identifier
|
462
|
+
# @option properties [String] :user_id Optional user ID. Verified by RabbitMQ against the actual connection username
|
463
|
+
# @option properties [String] :app_id Optional application ID
|
464
|
+
#
|
465
|
+
# @return [HotBunnies::Channel] Self
|
466
|
+
# @api public
|
153
467
|
def basic_publish(exchange, routing_key, mandatory, properties, body)
|
154
468
|
converting_rjc_exceptions_to_ruby do
|
155
469
|
@delegate.basic_publish(exchange, routing_key, mandatory, false, BasicPropertiesBuilder.build_properties_from(properties || Hash.new), body)
|
@@ -181,10 +495,24 @@ module HotBunnies
|
|
181
495
|
end
|
182
496
|
end
|
183
497
|
|
498
|
+
# Sets how many messages will be given to consumers on this channel before they
|
499
|
+
# have to acknowledge or reject one of the previously consumed messages
|
500
|
+
#
|
501
|
+
# @param [Integer] prefetch_count Prefetch (QoS setting) for this channel
|
502
|
+
# @see http://hotbunnies.info/articles/exchanges.html Exchanges and Publishing guide
|
503
|
+
# @see http://hotbunnies.info/articles/queues.html Queues and Consumers guide
|
504
|
+
# @api public
|
184
505
|
def prefetch=(n)
|
185
506
|
basic_qos(n)
|
186
507
|
end
|
187
508
|
|
509
|
+
# Acknowledges a message. Acknowledged messages are completely removed from the queue.
|
510
|
+
#
|
511
|
+
# @param [Integer] delivery_tag Delivery tag to acknowledge
|
512
|
+
# @param [Boolean] multiple (false) Should all unacknowledged messages up to this be acknowledged as well?
|
513
|
+
# @see #nack
|
514
|
+
# @see http://hotbunnies.info/articles/queues.html Queues and Consumers guide
|
515
|
+
# @api public
|
188
516
|
def ack(delivery_tag, multiple = false)
|
189
517
|
converting_rjc_exceptions_to_ruby do
|
190
518
|
basic_ack(delivery_tag, multiple)
|
@@ -192,24 +520,120 @@ module HotBunnies
|
|
192
520
|
end
|
193
521
|
alias acknowledge ack
|
194
522
|
|
523
|
+
# Rejects a message. A rejected message can be requeued or
|
524
|
+
# dropped by RabbitMQ.
|
525
|
+
#
|
526
|
+
# @param [Integer] delivery_tag Delivery tag to reject
|
527
|
+
# @param [Boolean] requeue Should this message be requeued instead of dropping it?
|
528
|
+
# @see #ack
|
529
|
+
# @see #nack
|
530
|
+
# @see http://hotbunnies.info/articles/queues.html Queues and Consumers guide
|
531
|
+
# @api public
|
195
532
|
def reject(delivery_tag, requeue = false)
|
196
533
|
converting_rjc_exceptions_to_ruby do
|
197
534
|
basic_reject(delivery_tag, requeue)
|
198
535
|
end
|
199
536
|
end
|
200
537
|
|
538
|
+
# Rejects a message. A rejected message can be requeued or
|
539
|
+
# dropped by RabbitMQ. This method is similar to {Bunny::Channel#reject} but
|
540
|
+
# supports rejecting multiple messages at once, and is usually preferred.
|
541
|
+
#
|
542
|
+
# @param [Integer] delivery_tag Delivery tag to reject
|
543
|
+
# @param [Boolean] multiple (false) Should all unacknowledged messages up to this be rejected as well?
|
544
|
+
# @param [Boolean] requeue (false) Should this message be requeued instead of dropping it?
|
545
|
+
# @see #ack
|
546
|
+
# @see http://hotbunnies.info/articles/queues.html Queues and Consumers guide
|
547
|
+
# @api public
|
201
548
|
def nack(delivery_tag, multiple = false, requeue = false)
|
202
549
|
converting_rjc_exceptions_to_ruby do
|
203
550
|
basic_nack(delivery_tag, multiple, requeue)
|
204
551
|
end
|
205
552
|
end
|
206
553
|
|
554
|
+
# Rejects or requeues a message.
|
555
|
+
#
|
556
|
+
# @param [Integer] delivery_tag Delivery tag obtained from delivery info
|
557
|
+
# @param [Boolean] requeue Should the message be requeued?
|
558
|
+
# @return [NilClass] nil
|
559
|
+
#
|
560
|
+
# @example Requeue a message
|
561
|
+
# conn = Bunny.new
|
562
|
+
# conn.start
|
563
|
+
#
|
564
|
+
# ch = conn.create_channel
|
565
|
+
# q.subscribe do |delivery_info, properties, payload|
|
566
|
+
# # requeue the message
|
567
|
+
# ch.basic_reject(delivery_info.delivery_tag, true)
|
568
|
+
# end
|
569
|
+
#
|
570
|
+
# @example Reject a message
|
571
|
+
# conn = Bunny.new
|
572
|
+
# conn.start
|
573
|
+
#
|
574
|
+
# ch = conn.create_channel
|
575
|
+
# q.subscribe do |delivery_info, properties, payload|
|
576
|
+
# # requeue the message
|
577
|
+
# ch.basic_reject(delivery_info.delivery_tag, false)
|
578
|
+
# end
|
579
|
+
#
|
580
|
+
# @example Requeue a message fetched via basic.get
|
581
|
+
# conn = Bunny.new
|
582
|
+
# conn.start
|
583
|
+
#
|
584
|
+
# ch = conn.create_channel
|
585
|
+
# # we assume the queue exists and has messages
|
586
|
+
# delivery_info, properties, payload = ch.basic_get("bunny.examples.queue3", :ack => true)
|
587
|
+
# ch.basic_reject(delivery_info.delivery_tag, true)
|
588
|
+
#
|
589
|
+
# @see #basic_nack
|
590
|
+
# @see http://hotbunnies.info/articles/queues.html Queues and Consumers guide
|
591
|
+
# @api public
|
592
|
+
def basic_reject(delivery_tag, requeue)
|
593
|
+
converting_rjc_exceptions_to_ruby do
|
594
|
+
@delegate.basic_reject(delivery_tag, requeue)
|
595
|
+
end
|
596
|
+
end
|
597
|
+
|
598
|
+
def basic_ack(delivery_tag, multiple)
|
599
|
+
converting_rjc_exceptions_to_ruby do
|
600
|
+
@delegate.basic_ack(delivery_tag, multiple)
|
601
|
+
end
|
602
|
+
end
|
603
|
+
|
604
|
+
# Rejects or requeues messages just like {Bunny::Channel#basic_reject} but can do so
|
605
|
+
# with multiple messages at once.
|
606
|
+
#
|
607
|
+
# @param [Integer] delivery_tag Delivery tag obtained from delivery info
|
608
|
+
# @param [Boolean] requeue Should the message be requeued?
|
609
|
+
# @param [Boolean] multiple Should all deliveries up to this one be rejected/requeued?
|
610
|
+
# @return [NilClass] nil
|
611
|
+
#
|
612
|
+
# @see http://hotbunnies.info/articles/queues.html Queues and Consumers guide
|
613
|
+
# @see http://hotbunnies.info/articles/extensions.html RabbitMQ Extensions guide
|
614
|
+
# @api public
|
615
|
+
def basic_nack(delivery_tag, multiple = false, requeue = false)
|
616
|
+
converting_rjc_exceptions_to_ruby do
|
617
|
+
@delegate.basic_nack(delivery_tag, multiple, requeue)
|
618
|
+
end
|
619
|
+
end
|
620
|
+
|
621
|
+
# Redeliver unacknowledged messages
|
622
|
+
#
|
623
|
+
# @param [Boolean] requeue Should messages be requeued?
|
624
|
+
# @return RabbitMQ response
|
625
|
+
# @api public
|
207
626
|
def basic_recover(requeue = true)
|
208
627
|
converting_rjc_exceptions_to_ruby do
|
209
628
|
@delegate.basic_recover(requeue)
|
210
629
|
end
|
211
630
|
end
|
212
631
|
|
632
|
+
# Redeliver unacknowledged messages
|
633
|
+
#
|
634
|
+
# @param [Boolean] requeue Should messages be requeued?
|
635
|
+
# @return RabbitMQ response
|
636
|
+
# @api public
|
213
637
|
def basic_recover_async(requeue = true)
|
214
638
|
converting_rjc_exceptions_to_ruby do
|
215
639
|
@delegate.basic_recover_async(requeue)
|
@@ -1,209 +1,5 @@
|
|
1
|
-
|
2
|
-
import com.rabbitmq.client.DefaultConsumer
|
1
|
+
require "hot_bunnies/thread_pools"
|
3
2
|
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
def initialize(channel)
|
8
|
-
super(channel)
|
9
|
-
@channel = channel
|
10
|
-
|
11
|
-
@cancelling = JavaConcurrent::AtomicBoolean.new
|
12
|
-
@cancelled = JavaConcurrent::AtomicBoolean.new
|
13
|
-
|
14
|
-
@terminated = JavaConcurrent::AtomicBoolean.new
|
15
|
-
end
|
16
|
-
|
17
|
-
def handleDelivery(consumer_tag, envelope, properties, body)
|
18
|
-
body = String.from_java_bytes(body)
|
19
|
-
headers = Headers.new(channel, consumer_tag, envelope, properties)
|
20
|
-
|
21
|
-
deliver(headers, body)
|
22
|
-
end
|
23
|
-
|
24
|
-
def handleCancel(consumer_tag)
|
25
|
-
@cancelled.set(true)
|
26
|
-
@channel.unregister_consumer(consumer_tag)
|
27
|
-
|
28
|
-
if f = @opts[:on_cancellation]
|
29
|
-
case f.arity
|
30
|
-
when 0 then
|
31
|
-
f.call
|
32
|
-
when 1 then
|
33
|
-
f.call(self)
|
34
|
-
when 2 then
|
35
|
-
f.call(@channel, self)
|
36
|
-
when 3 then
|
37
|
-
f.call(@channel, self, consumer_tag)
|
38
|
-
else
|
39
|
-
f.call(@channel, self, consumer_tag)
|
40
|
-
end
|
41
|
-
end
|
42
|
-
|
43
|
-
@terminated.set(true)
|
44
|
-
end
|
45
|
-
|
46
|
-
def handleCancelOk(consumer_tag)
|
47
|
-
@cancelled.set(true)
|
48
|
-
@channel.unregister_consumer(consumer_tag)
|
49
|
-
|
50
|
-
@terminated.set(true)
|
51
|
-
end
|
52
|
-
|
53
|
-
def start
|
54
|
-
end
|
55
|
-
|
56
|
-
def deliver(headers, message)
|
57
|
-
raise NotImplementedError, 'To be implemented by a subclass'
|
58
|
-
end
|
59
|
-
|
60
|
-
def cancel
|
61
|
-
@cancelling.set(true)
|
62
|
-
response = channel.basic_cancel(consumer_tag)
|
63
|
-
@cancelled.set(true)
|
64
|
-
@terminated.set(true)
|
65
|
-
|
66
|
-
response
|
67
|
-
end
|
68
|
-
|
69
|
-
def cancelled?
|
70
|
-
@cancelling.get || @cancelled.get
|
71
|
-
end
|
72
|
-
|
73
|
-
def active?
|
74
|
-
!terminated?
|
75
|
-
end
|
76
|
-
|
77
|
-
def terminated?
|
78
|
-
@terminated.get
|
79
|
-
end
|
80
|
-
end
|
81
|
-
|
82
|
-
|
83
|
-
class CallbackConsumer < BaseConsumer
|
84
|
-
def initialize(channel, callback)
|
85
|
-
raise ArgumentError, "callback must not be nil!" if callback.nil?
|
86
|
-
|
87
|
-
super(channel)
|
88
|
-
@callback = callback
|
89
|
-
@callback_arity = @callback.arity
|
90
|
-
end
|
91
|
-
|
92
|
-
def callback(headers, message)
|
93
|
-
if @callback_arity == 2
|
94
|
-
@callback.call(headers, message)
|
95
|
-
else
|
96
|
-
@callback.call(message)
|
97
|
-
end
|
98
|
-
end
|
99
|
-
end
|
100
|
-
|
101
|
-
class AsyncCallbackConsumer < CallbackConsumer
|
102
|
-
def initialize(channel, opts, callback, executor)
|
103
|
-
super(channel, callback)
|
104
|
-
@executor = executor
|
105
|
-
@executor_submit = executor.java_method(:submit, [JavaConcurrent::Runnable.java_class])
|
106
|
-
@opts = opts
|
107
|
-
end
|
108
|
-
|
109
|
-
def deliver(headers, message)
|
110
|
-
unless @executor.shutdown?
|
111
|
-
@executor_submit.call do
|
112
|
-
begin
|
113
|
-
callback(headers, message)
|
114
|
-
rescue Exception => e
|
115
|
-
$stderr.puts "Unhandled exception in consumer #{@consumer_tag}: #{e.message}"
|
116
|
-
end
|
117
|
-
end
|
118
|
-
end
|
119
|
-
end
|
120
|
-
|
121
|
-
def cancel
|
122
|
-
super
|
123
|
-
|
124
|
-
gracefully_shutdown
|
125
|
-
end
|
126
|
-
|
127
|
-
def handleCancel(consumer_tag)
|
128
|
-
super(consumer_tag)
|
129
|
-
|
130
|
-
gracefully_shutdown
|
131
|
-
end
|
132
|
-
|
133
|
-
def shutdown!
|
134
|
-
@executor.shutdown_now if @executor
|
135
|
-
end
|
136
|
-
alias shut_down! shutdown!
|
137
|
-
|
138
|
-
def gracefully_shut_down
|
139
|
-
unless @executor.await_termination(1, JavaConcurrent::TimeUnit::SECONDS)
|
140
|
-
@executor.shutdown_now
|
141
|
-
end
|
142
|
-
@terminated.set(true)
|
143
|
-
end
|
144
|
-
alias maybe_shut_down_executor gracefully_shut_down
|
145
|
-
alias gracefully_shutdown gracefully_shut_down
|
146
|
-
end
|
147
|
-
|
148
|
-
class BlockingCallbackConsumer < CallbackConsumer
|
149
|
-
include JavaConcurrent
|
150
|
-
|
151
|
-
POISON = :__poison__
|
152
|
-
|
153
|
-
def initialize(channel, buffer_size, opts, callback)
|
154
|
-
super(channel, callback)
|
155
|
-
if buffer_size
|
156
|
-
@internal_queue = ArrayBlockingQueue.new(buffer_size)
|
157
|
-
else
|
158
|
-
@internal_queue = LinkedBlockingQueue.new
|
159
|
-
end
|
160
|
-
|
161
|
-
@opts = opts
|
162
|
-
end
|
163
|
-
|
164
|
-
def start
|
165
|
-
interrupted = false
|
166
|
-
until (@cancelling.get || @cancelled.get) || JavaConcurrent::Thread.current_thread.interrupted?
|
167
|
-
begin
|
168
|
-
pair = @internal_queue.take
|
169
|
-
if pair
|
170
|
-
if pair == POISON
|
171
|
-
@cancelling.set(true)
|
172
|
-
else
|
173
|
-
callback(*pair)
|
174
|
-
end
|
175
|
-
end
|
176
|
-
rescue InterruptedException => e
|
177
|
-
interrupted = true
|
178
|
-
end
|
179
|
-
end
|
180
|
-
while (pair = @internal_queue.poll)
|
181
|
-
callback(*pair)
|
182
|
-
end
|
183
|
-
@terminated.set(true)
|
184
|
-
if interrupted
|
185
|
-
JavaConcurrent::Thread.current_thread.interrupt
|
186
|
-
end
|
187
|
-
end
|
188
|
-
|
189
|
-
def deliver(*pair)
|
190
|
-
if (@cancelling.get || @cancelled.get) || JavaConcurrent::Thread.current_thread.interrupted?
|
191
|
-
@internal_queue.offer(pair)
|
192
|
-
else
|
193
|
-
begin
|
194
|
-
@internal_queue.put(pair)
|
195
|
-
rescue InterruptedException => e
|
196
|
-
JavaConcurrent::Thread.current_thread.interrupt
|
197
|
-
end
|
198
|
-
end
|
199
|
-
end
|
200
|
-
|
201
|
-
def gracefully_shut_down
|
202
|
-
@cancelling.set(true)
|
203
|
-
@internal_queue.offer(POISON)
|
204
|
-
|
205
|
-
@terminated.set(true)
|
206
|
-
end
|
207
|
-
|
208
|
-
end
|
209
|
-
end
|
3
|
+
require "hot_bunnies/consumers/base"
|
4
|
+
require "hot_bunnies/consumers/non_blocking"
|
5
|
+
require "hot_bunnies/consumers/blocking"
|
@@ -0,0 +1,72 @@
|
|
1
|
+
module HotBunnies
|
2
|
+
import com.rabbitmq.client.DefaultConsumer
|
3
|
+
|
4
|
+
class BaseConsumer < DefaultConsumer
|
5
|
+
attr_accessor :consumer_tag
|
6
|
+
|
7
|
+
def initialize(channel)
|
8
|
+
super(channel)
|
9
|
+
@channel = channel
|
10
|
+
|
11
|
+
@cancelling = JavaConcurrent::AtomicBoolean.new
|
12
|
+
@cancelled = JavaConcurrent::AtomicBoolean.new
|
13
|
+
|
14
|
+
@terminated = JavaConcurrent::AtomicBoolean.new
|
15
|
+
end
|
16
|
+
|
17
|
+
def handleDelivery(consumer_tag, envelope, properties, body)
|
18
|
+
body = String.from_java_bytes(body)
|
19
|
+
headers = Headers.new(channel, consumer_tag, envelope, properties)
|
20
|
+
|
21
|
+
deliver(headers, body)
|
22
|
+
end
|
23
|
+
|
24
|
+
def handleCancel(consumer_tag)
|
25
|
+
@cancelled.set(true)
|
26
|
+
@channel.unregister_consumer(consumer_tag)
|
27
|
+
|
28
|
+
if f = @opts[:on_cancellation]
|
29
|
+
case f.arity
|
30
|
+
when 0 then
|
31
|
+
f.call
|
32
|
+
when 1 then
|
33
|
+
f.call(self)
|
34
|
+
when 2 then
|
35
|
+
f.call(@channel, self)
|
36
|
+
when 3 then
|
37
|
+
f.call(@channel, self, consumer_tag)
|
38
|
+
else
|
39
|
+
f.call(@channel, self, consumer_tag)
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
@terminated.set(true)
|
44
|
+
end
|
45
|
+
|
46
|
+
def handleCancelOk(consumer_tag)
|
47
|
+
@cancelled.set(true)
|
48
|
+
@channel.unregister_consumer(consumer_tag)
|
49
|
+
|
50
|
+
@terminated.set(true)
|
51
|
+
end
|
52
|
+
|
53
|
+
def start
|
54
|
+
end
|
55
|
+
|
56
|
+
def deliver(headers, message)
|
57
|
+
raise NotImplementedError, 'To be implemented by a subclass'
|
58
|
+
end
|
59
|
+
|
60
|
+
def cancelled?
|
61
|
+
@cancelling.get || @cancelled.get
|
62
|
+
end
|
63
|
+
|
64
|
+
def active?
|
65
|
+
!terminated?
|
66
|
+
end
|
67
|
+
|
68
|
+
def terminated?
|
69
|
+
@terminated.get
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
@@ -0,0 +1,82 @@
|
|
1
|
+
require "hot_bunnies/consumers/base"
|
2
|
+
|
3
|
+
module HotBunnies
|
4
|
+
class BlockingCallbackConsumer < CallbackConsumer
|
5
|
+
include JavaConcurrent
|
6
|
+
|
7
|
+
POISON = :__poison__
|
8
|
+
|
9
|
+
def initialize(channel, buffer_size, opts, callback)
|
10
|
+
super(channel, callback)
|
11
|
+
if buffer_size
|
12
|
+
@internal_queue = ArrayBlockingQueue.new(buffer_size)
|
13
|
+
else
|
14
|
+
@internal_queue = LinkedBlockingQueue.new
|
15
|
+
end
|
16
|
+
|
17
|
+
@opts = opts
|
18
|
+
end
|
19
|
+
|
20
|
+
def cancel
|
21
|
+
@cancelling.set(true)
|
22
|
+
response = channel.basic_cancel(consumer_tag)
|
23
|
+
@cancelled.set(true)
|
24
|
+
|
25
|
+
@internal_queue.offer(POISON)
|
26
|
+
@terminated.set(true)
|
27
|
+
|
28
|
+
response
|
29
|
+
end
|
30
|
+
|
31
|
+
def start
|
32
|
+
interrupted = false
|
33
|
+
until (@cancelling.get || @cancelled.get) || JavaConcurrent::Thread.current_thread.interrupted?
|
34
|
+
begin
|
35
|
+
pair = @internal_queue.take
|
36
|
+
if pair
|
37
|
+
if pair == POISON
|
38
|
+
@cancelling.set(true)
|
39
|
+
else
|
40
|
+
callback(*pair)
|
41
|
+
end
|
42
|
+
end
|
43
|
+
rescue InterruptedException => e
|
44
|
+
interrupted = true
|
45
|
+
end
|
46
|
+
end
|
47
|
+
while (pair = @internal_queue.poll)
|
48
|
+
if pair
|
49
|
+
if pair == POISON
|
50
|
+
@cancelling.set(true)
|
51
|
+
else
|
52
|
+
callback(*pair)
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
@terminated.set(true)
|
57
|
+
if interrupted
|
58
|
+
JavaConcurrent::Thread.current_thread.interrupt
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
def deliver(*pair)
|
63
|
+
if (@cancelling.get || @cancelled.get) || JavaConcurrent::Thread.current_thread.interrupted?
|
64
|
+
@internal_queue.offer(pair)
|
65
|
+
else
|
66
|
+
begin
|
67
|
+
@internal_queue.put(pair)
|
68
|
+
rescue InterruptedException => e
|
69
|
+
JavaConcurrent::Thread.current_thread.interrupt
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
def gracefully_shut_down
|
75
|
+
@cancelling.set(true)
|
76
|
+
@internal_queue.offer(POISON)
|
77
|
+
|
78
|
+
@terminated.set(true)
|
79
|
+
end
|
80
|
+
|
81
|
+
end
|
82
|
+
end
|
@@ -0,0 +1,73 @@
|
|
1
|
+
require "hot_bunnies/consumers/base"
|
2
|
+
|
3
|
+
module HotBunnies
|
4
|
+
class CallbackConsumer < BaseConsumer
|
5
|
+
def initialize(channel, callback)
|
6
|
+
raise ArgumentError, "callback must not be nil!" if callback.nil?
|
7
|
+
|
8
|
+
super(channel)
|
9
|
+
@callback = callback
|
10
|
+
@callback_arity = @callback.arity
|
11
|
+
end
|
12
|
+
|
13
|
+
def callback(headers, message)
|
14
|
+
if @callback_arity == 2
|
15
|
+
@callback.call(headers, message)
|
16
|
+
else
|
17
|
+
@callback.call(message)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
class AsyncCallbackConsumer < CallbackConsumer
|
23
|
+
def initialize(channel, opts, callback, executor)
|
24
|
+
super(channel, callback)
|
25
|
+
@executor = executor
|
26
|
+
@executor_submit = executor.java_method(:submit, [JavaConcurrent::Runnable.java_class])
|
27
|
+
@opts = opts
|
28
|
+
end
|
29
|
+
|
30
|
+
def deliver(headers, message)
|
31
|
+
unless @executor.shutdown?
|
32
|
+
@executor_submit.call do
|
33
|
+
begin
|
34
|
+
callback(headers, message)
|
35
|
+
rescue Exception => e
|
36
|
+
$stderr.puts "Unhandled exception in consumer #{@consumer_tag}: #{e.message}"
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
def cancel
|
43
|
+
@cancelling.set(true)
|
44
|
+
response = channel.basic_cancel(consumer_tag)
|
45
|
+
@cancelled.set(true)
|
46
|
+
@terminated.set(true)
|
47
|
+
|
48
|
+
gracefully_shutdown
|
49
|
+
|
50
|
+
response
|
51
|
+
end
|
52
|
+
|
53
|
+
def handleCancel(consumer_tag)
|
54
|
+
super(consumer_tag)
|
55
|
+
|
56
|
+
gracefully_shutdown
|
57
|
+
end
|
58
|
+
|
59
|
+
def shutdown!
|
60
|
+
@executor.shutdown_now if @executor
|
61
|
+
end
|
62
|
+
alias shut_down! shutdown!
|
63
|
+
|
64
|
+
def gracefully_shut_down
|
65
|
+
unless @executor.await_termination(1, JavaConcurrent::TimeUnit::SECONDS)
|
66
|
+
@executor.shutdown_now
|
67
|
+
end
|
68
|
+
@terminated.set(true)
|
69
|
+
end
|
70
|
+
alias maybe_shut_down_executor gracefully_shut_down
|
71
|
+
alias gracefully_shutdown gracefully_shut_down
|
72
|
+
end
|
73
|
+
end
|
data/lib/hot_bunnies/session.rb
CHANGED
@@ -6,6 +6,16 @@ module HotBunnies
|
|
6
6
|
java_import com.rabbitmq.client.Connection
|
7
7
|
java_import java.util.concurrent.ConcurrentHashMap
|
8
8
|
|
9
|
+
# Connection to a RabbitMQ node.
|
10
|
+
#
|
11
|
+
# Used to open and close connections and open (create) new channels.
|
12
|
+
#
|
13
|
+
# @see .connect
|
14
|
+
# @see #create_channel
|
15
|
+
# @see #close
|
16
|
+
# @api public
|
17
|
+
# @see http://hotbunnies.info/articles/getting_started.html Getting Started guide
|
18
|
+
# @see http://hotbunnies.info/articles/connecting.html Connecting to RabbitMQ guide
|
9
19
|
class Session
|
10
20
|
|
11
21
|
#
|
@@ -14,13 +24,26 @@ module HotBunnies
|
|
14
24
|
|
15
25
|
# Connects to a RabbitMQ node.
|
16
26
|
#
|
27
|
+
# @param [Hash] options Connection options
|
28
|
+
#
|
29
|
+
# @option options [String] :host ("127.0.0.1") Hostname or IP address to connect to
|
30
|
+
# @option options [Integer] :port (5672) Port RabbitMQ listens on
|
31
|
+
# @option options [String] :username ("guest") Username
|
32
|
+
# @option options [String] :password ("guest") Password
|
33
|
+
# @option options [String] :vhost ("/") Virtual host to use
|
34
|
+
# @option options [Integer] :heartbeat (600) Heartbeat interval. 0 means no heartbeat.
|
35
|
+
# @option options [Boolean] :tls (false) Set to true to use TLS/SSL connection. This will switch port to 5671 by default.
|
36
|
+
#
|
37
|
+
# @see http://hotbunnies.info/articles/connecting.html Connecting to RabbitMQ guide
|
38
|
+
#
|
39
|
+
#
|
17
40
|
# @api public
|
18
41
|
def self.connect(options={})
|
19
42
|
cf = ConnectionFactory.new
|
20
43
|
|
21
44
|
cf.uri = options[:uri] if options[:uri]
|
22
45
|
cf.host = hostname_from(options) if include_host?(options)
|
23
|
-
cf.port = options[:port]
|
46
|
+
cf.port = options[:port].to_i if options[:port]
|
24
47
|
cf.virtual_host = vhost_from(options) if include_vhost?(options)
|
25
48
|
cf.connection_timeout = timeout_from(options) if include_timeout?(options)
|
26
49
|
cf.username = username_from(options) if include_username?(options)
|
@@ -45,9 +68,13 @@ module HotBunnies
|
|
45
68
|
new(cf)
|
46
69
|
end
|
47
70
|
|
48
|
-
|
71
|
+
# @private
|
72
|
+
attr_reader :thread
|
73
|
+
# @return [Array<HotBunnies::Channel>] Channels opened on this connection
|
74
|
+
attr_reader :channels
|
49
75
|
|
50
76
|
|
77
|
+
# @private
|
51
78
|
def initialize(connection_factory)
|
52
79
|
@cf = connection_factory
|
53
80
|
@connection = converting_rjc_exceptions_to_ruby do
|
@@ -58,6 +85,15 @@ module HotBunnies
|
|
58
85
|
@thread = Thread.current
|
59
86
|
end
|
60
87
|
|
88
|
+
# Opens a new channel.
|
89
|
+
#
|
90
|
+
# @param [Integer] (nil): Channel number. Pass nil to let HotBunnies allocate an available number
|
91
|
+
# in a safe way.
|
92
|
+
#
|
93
|
+
# @return [HotBunnies::Channel] Newly created channel
|
94
|
+
# @see HotBunnies::Channel
|
95
|
+
# @see http://hotbunnies.info/articles/getting_started.html Getting Started guide
|
96
|
+
# @api public
|
61
97
|
def create_channel(n = nil)
|
62
98
|
jc = if n
|
63
99
|
@connection.create_channel(n)
|
@@ -86,7 +122,7 @@ module HotBunnies
|
|
86
122
|
sh
|
87
123
|
end
|
88
124
|
|
89
|
-
|
125
|
+
# Flushes the socket used by this connection.
|
90
126
|
def flush
|
91
127
|
@connection.flush
|
92
128
|
end
|
@@ -95,6 +131,7 @@ module HotBunnies
|
|
95
131
|
@connection.heartbeat = n
|
96
132
|
end
|
97
133
|
|
134
|
+
# No-op, exists for better API compatibility with Bunny.
|
98
135
|
def start
|
99
136
|
# no-op
|
100
137
|
#
|
@@ -125,64 +162,79 @@ module HotBunnies
|
|
125
162
|
@channels[ch.channel_number] = ch
|
126
163
|
end
|
127
164
|
|
165
|
+
# @private
|
128
166
|
def unregister_channel(ch)
|
129
167
|
@channels.delete(ch.channel_number)
|
130
168
|
end
|
131
169
|
|
132
170
|
protected
|
133
171
|
|
172
|
+
# @private
|
134
173
|
def self.hostname_from(options)
|
135
174
|
options[:host] || options[:hostname] || ConnectionFactory.DEFAULT_HOST
|
136
175
|
end
|
137
176
|
|
177
|
+
# @private
|
138
178
|
def self.include_host?(options)
|
139
179
|
!!(options[:host] || options[:hostname])
|
140
180
|
end
|
141
181
|
|
182
|
+
# @private
|
142
183
|
def self.vhost_from(options)
|
143
184
|
options[:virtual_host] || options[:vhost] || ConnectionFactory.DEFAULT_VHOST
|
144
185
|
end
|
145
186
|
|
187
|
+
# @private
|
146
188
|
def self.include_vhost?(options)
|
147
189
|
!!(options[:virtual_host] || options[:vhost])
|
148
190
|
end
|
149
191
|
|
192
|
+
# @private
|
150
193
|
def self.timeout_from(options)
|
151
194
|
options[:connection_timeout] || options[:timeout]
|
152
195
|
end
|
153
196
|
|
197
|
+
# @private
|
154
198
|
def self.include_timeout?(options)
|
155
199
|
!!(options[:connection_timeout] || options[:timeout])
|
156
200
|
end
|
157
201
|
|
202
|
+
# @private
|
158
203
|
def self.username_from(options)
|
159
204
|
options[:username] || options[:user] || ConnectionFactory.DEFAULT_USER
|
160
205
|
end
|
161
206
|
|
207
|
+
# @private
|
162
208
|
def self.heartbeat_from(options)
|
163
209
|
options[:heartbeat_interval] || options[:requested_heartbeat] || ConnectionFactory.DEFAULT_HEARTBEAT
|
164
210
|
end
|
165
211
|
|
212
|
+
# @private
|
166
213
|
def self.connection_timeout_from(options)
|
167
214
|
options[:connection_timeout_interval] || options[:connection_timeout] || ConnectionFactory.DEFAULT_CONNECTION_TIMEOUT
|
168
215
|
end
|
169
216
|
|
217
|
+
# @private
|
170
218
|
def self.include_username?(options)
|
171
219
|
!!(options[:username] || options[:user])
|
172
220
|
end
|
173
221
|
|
222
|
+
# @private
|
174
223
|
def self.password_from(options)
|
175
224
|
options[:password] || options[:pass] || ConnectionFactory.DEFAULT_PASS
|
176
225
|
end
|
177
226
|
|
227
|
+
# @private
|
178
228
|
def self.include_password?(options)
|
179
229
|
!!(options[:password] || options[:pass])
|
180
230
|
end
|
181
231
|
|
232
|
+
# @private
|
182
233
|
def self.include_heartbeat?(options)
|
183
234
|
!!(options[:heartbeat_interval] || options[:requested_heartbeat] || options[:heartbeat])
|
184
235
|
end
|
185
236
|
|
237
|
+
# @private
|
186
238
|
def self.include_connection_timeout?(options)
|
187
239
|
!!(options[:connection_timeout_interval] || options[:connection_timeout])
|
188
240
|
end
|
@@ -203,6 +255,7 @@ module HotBunnies
|
|
203
255
|
end
|
204
256
|
end
|
205
257
|
|
258
|
+
# @private
|
206
259
|
def new_connection
|
207
260
|
converting_rjc_exceptions_to_ruby do
|
208
261
|
@cf.new_connection
|
@@ -0,0 +1,32 @@
|
|
1
|
+
module HotBunnies
|
2
|
+
import java.util.concurrent.Executors
|
3
|
+
|
4
|
+
# A slighly more Ruby developer-friendly way of instantiating various
|
5
|
+
# JDK executors (thread pools).
|
6
|
+
class ThreadPools
|
7
|
+
# Returns a new thread pool (JDK executor) of a fixed size.
|
8
|
+
#
|
9
|
+
# @return A thread pool (JDK executor)
|
10
|
+
def self.fixed_of_size(n)
|
11
|
+
raise ArgumentError.new("n must be a positive integer!") unless Integer === n
|
12
|
+
raise ArgumentError.new("n must be a positive integer!") unless n > 0
|
13
|
+
|
14
|
+
Executors.new_fixed_thread_pool(n)
|
15
|
+
end
|
16
|
+
|
17
|
+
# Returns a new thread pool (JDK executor) of a fixed size of 1.
|
18
|
+
#
|
19
|
+
# @return A thread pool (JDK executor)
|
20
|
+
def self.single_threaded
|
21
|
+
Executors.new_single_thread_executor
|
22
|
+
end
|
23
|
+
|
24
|
+
# Returns a new thread pool (JDK executor) that will create new
|
25
|
+
# threads as needed.
|
26
|
+
#
|
27
|
+
# @return A thread pool (JDK executor)
|
28
|
+
def self.dynamically_growing
|
29
|
+
Executors.new_cached_thread_pool
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
data/lib/hot_bunnies/version.rb
CHANGED
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: hot_bunnies
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 2.0.0.
|
4
|
+
version: 2.0.0.pre9
|
5
5
|
prerelease: 6
|
6
6
|
platform: java
|
7
7
|
authors:
|
@@ -10,7 +10,7 @@ authors:
|
|
10
10
|
autorequire:
|
11
11
|
bindir: bin
|
12
12
|
cert_chain: []
|
13
|
-
date: 2013-07-
|
13
|
+
date: 2013-07-11 00:00:00.000000000 Z
|
14
14
|
dependencies: []
|
15
15
|
description: RabbitMQ client for JRuby built around the official RabbitMQ Java client
|
16
16
|
email:
|
@@ -24,6 +24,9 @@ files:
|
|
24
24
|
- lib/hot_bunnies.rb
|
25
25
|
- lib/hot_bunnies/channel.rb
|
26
26
|
- lib/hot_bunnies/consumers.rb
|
27
|
+
- lib/hot_bunnies/consumers/base.rb
|
28
|
+
- lib/hot_bunnies/consumers/blocking.rb
|
29
|
+
- lib/hot_bunnies/consumers/non_blocking.rb
|
27
30
|
- lib/hot_bunnies/exceptions.rb
|
28
31
|
- lib/hot_bunnies/exchange.rb
|
29
32
|
- lib/hot_bunnies/juc.rb
|
@@ -31,6 +34,7 @@ files:
|
|
31
34
|
- lib/hot_bunnies/queue.rb
|
32
35
|
- lib/hot_bunnies/session.rb
|
33
36
|
- lib/hot_bunnies/shutdown_listener.rb
|
37
|
+
- lib/hot_bunnies/thread_pools.rb
|
34
38
|
- lib/hot_bunnies/version.rb
|
35
39
|
homepage: http://hotbunnies.info
|
36
40
|
licenses: []
|