garaio_bunny 2.19.1
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.
- checksums.yaml +7 -0
- data/README.md +231 -0
- data/lib/amq/protocol/extensions.rb +16 -0
- data/lib/bunny/authentication/credentials_encoder.rb +55 -0
- data/lib/bunny/authentication/external_mechanism_encoder.rb +27 -0
- data/lib/bunny/authentication/plain_mechanism_encoder.rb +19 -0
- data/lib/bunny/channel.rb +2055 -0
- data/lib/bunny/channel_id_allocator.rb +82 -0
- data/lib/bunny/concurrent/atomic_fixnum.rb +75 -0
- data/lib/bunny/concurrent/condition.rb +66 -0
- data/lib/bunny/concurrent/continuation_queue.rb +62 -0
- data/lib/bunny/concurrent/linked_continuation_queue.rb +61 -0
- data/lib/bunny/concurrent/synchronized_sorted_set.rb +56 -0
- data/lib/bunny/consumer.rb +128 -0
- data/lib/bunny/consumer_tag_generator.rb +23 -0
- data/lib/bunny/consumer_work_pool.rb +122 -0
- data/lib/bunny/cruby/socket.rb +110 -0
- data/lib/bunny/cruby/ssl_socket.rb +118 -0
- data/lib/bunny/delivery_info.rb +93 -0
- data/lib/bunny/exceptions.rb +269 -0
- data/lib/bunny/exchange.rb +275 -0
- data/lib/bunny/framing.rb +56 -0
- data/lib/bunny/get_response.rb +83 -0
- data/lib/bunny/heartbeat_sender.rb +71 -0
- data/lib/bunny/jruby/socket.rb +57 -0
- data/lib/bunny/jruby/ssl_socket.rb +58 -0
- data/lib/bunny/message_properties.rb +119 -0
- data/lib/bunny/queue.rb +393 -0
- data/lib/bunny/reader_loop.rb +158 -0
- data/lib/bunny/return_info.rb +74 -0
- data/lib/bunny/session.rb +1483 -0
- data/lib/bunny/socket.rb +14 -0
- data/lib/bunny/ssl_socket.rb +14 -0
- data/lib/bunny/test_kit.rb +41 -0
- data/lib/bunny/timeout.rb +7 -0
- data/lib/bunny/transport.rb +526 -0
- data/lib/bunny/version.rb +6 -0
- data/lib/bunny/versioned_delivery_tag.rb +28 -0
- data/lib/bunny.rb +92 -0
- metadata +127 -0
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
require "thread"
|
|
2
|
+
require "monitor"
|
|
3
|
+
require "amq/int_allocator"
|
|
4
|
+
|
|
5
|
+
module Bunny
|
|
6
|
+
# Bitset-based channel id allocator. When channels are closed,
|
|
7
|
+
# ids are released back to the pool.
|
|
8
|
+
#
|
|
9
|
+
# Every connection has its own allocator.
|
|
10
|
+
#
|
|
11
|
+
# Allocating and releasing ids is synchronized and can be performed
|
|
12
|
+
# from multiple threads.
|
|
13
|
+
class ChannelIdAllocator
|
|
14
|
+
|
|
15
|
+
#
|
|
16
|
+
# API
|
|
17
|
+
#
|
|
18
|
+
|
|
19
|
+
# @param [Integer] max_channel Max allowed channel id
|
|
20
|
+
def initialize(max_channel = ((1 << 11) - 1))
|
|
21
|
+
# channel 0 has special meaning in the protocol, so start
|
|
22
|
+
# allocator at 1
|
|
23
|
+
@allocator = AMQ::IntAllocator.new(1, max_channel)
|
|
24
|
+
@mutex = Monitor.new
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
# Returns next available channel id. This method is thread safe.
|
|
29
|
+
#
|
|
30
|
+
# @return [Integer]
|
|
31
|
+
# @api public
|
|
32
|
+
# @see ChannelManager#release_channel_id
|
|
33
|
+
# @see ChannelManager#reset_channel_id_allocator
|
|
34
|
+
def next_channel_id
|
|
35
|
+
@mutex.synchronize do
|
|
36
|
+
@allocator.allocate
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
# Releases previously allocated channel id. This method is thread safe.
|
|
41
|
+
#
|
|
42
|
+
# @param [Integer] i Channel id to release
|
|
43
|
+
# @api public
|
|
44
|
+
# @see ChannelManager#next_channel_id
|
|
45
|
+
# @see ChannelManager#reset_channel_id_allocator
|
|
46
|
+
def release_channel_id(i)
|
|
47
|
+
@mutex.synchronize do
|
|
48
|
+
@allocator.release(i)
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
# Returns true if given channel id has been previously allocated and not yet released.
|
|
54
|
+
# This method is thread safe.
|
|
55
|
+
#
|
|
56
|
+
# @param [Integer] i Channel id to check
|
|
57
|
+
# @return [Boolean] true if given channel id has been previously allocated and not yet released
|
|
58
|
+
# @api public
|
|
59
|
+
# @see ChannelManager#next_channel_id
|
|
60
|
+
# @see ChannelManager#release_channel_id
|
|
61
|
+
def allocated_channel_id?(i)
|
|
62
|
+
@mutex.synchronize do
|
|
63
|
+
@allocator.allocated?(i)
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
# Resets channel allocator. This method is thread safe.
|
|
68
|
+
# @api public
|
|
69
|
+
# @see Channel.next_channel_id
|
|
70
|
+
# @see Channel.release_channel_id
|
|
71
|
+
def reset_channel_id_allocator
|
|
72
|
+
@mutex.synchronize do
|
|
73
|
+
@allocator.reset
|
|
74
|
+
end
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
# @private
|
|
78
|
+
def synchronize(&block)
|
|
79
|
+
@mutex.synchronize(&block)
|
|
80
|
+
end
|
|
81
|
+
end
|
|
82
|
+
end
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
require "set"
|
|
2
|
+
require "thread"
|
|
3
|
+
require "monitor"
|
|
4
|
+
|
|
5
|
+
module Bunny
|
|
6
|
+
module Concurrent
|
|
7
|
+
# Minimalistic implementation of a synchronized fixnum value,
|
|
8
|
+
# designed after (but not implementing the entire API of!)
|
|
9
|
+
#
|
|
10
|
+
# @note Designed to be intentionally minimalistic and only cover Bunny's needs.
|
|
11
|
+
#
|
|
12
|
+
# @api public
|
|
13
|
+
class AtomicFixnum
|
|
14
|
+
def initialize(n = 0)
|
|
15
|
+
@n = n
|
|
16
|
+
@mutex = Monitor.new
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def get
|
|
20
|
+
@mutex.synchronize do
|
|
21
|
+
@n
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
alias to_i get
|
|
25
|
+
|
|
26
|
+
def set(n)
|
|
27
|
+
@mutex.synchronize do
|
|
28
|
+
@n = n
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def increment
|
|
33
|
+
@mutex.synchronize do
|
|
34
|
+
@n = @n + 1
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
alias inc increment
|
|
38
|
+
alias increment_and_get increment
|
|
39
|
+
|
|
40
|
+
def get_and_add(i)
|
|
41
|
+
@mutex.synchronize do
|
|
42
|
+
v = @n
|
|
43
|
+
@n = @n + i
|
|
44
|
+
|
|
45
|
+
v
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def get_and_increment
|
|
50
|
+
@mutex.synchronize do
|
|
51
|
+
v = @n
|
|
52
|
+
@n = @n + 1
|
|
53
|
+
|
|
54
|
+
v
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
def decrement
|
|
59
|
+
@mutex.synchronize do
|
|
60
|
+
@n = @n - 1
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
alias dec decrement
|
|
64
|
+
alias decrement_and_get decrement
|
|
65
|
+
|
|
66
|
+
def ==(m)
|
|
67
|
+
@mutex.synchronize { @n == m }
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
def ===(v)
|
|
71
|
+
@mutex.synchronize { @n === v }
|
|
72
|
+
end
|
|
73
|
+
end
|
|
74
|
+
end
|
|
75
|
+
end
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
require "thread"
|
|
2
|
+
require "monitor"
|
|
3
|
+
|
|
4
|
+
module Bunny
|
|
5
|
+
# @private
|
|
6
|
+
module Concurrent
|
|
7
|
+
# Akin to java.util.concurrent.Condition and intrinsic object monitors (Object#wait, Object#notify, Object#notifyAll) in Java:
|
|
8
|
+
# threads can wait (block until notified) on a condition other threads notify them about.
|
|
9
|
+
# Unlike the j.u.c. version, this one has a single waiting set.
|
|
10
|
+
#
|
|
11
|
+
# Conditions can optionally be annotated with a description string for ease of debugging.
|
|
12
|
+
# @private
|
|
13
|
+
class Condition
|
|
14
|
+
attr_reader :waiting_threads, :description
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def initialize(description = nil)
|
|
18
|
+
@mutex = Monitor.new
|
|
19
|
+
@waiting_threads = []
|
|
20
|
+
@description = description
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def wait
|
|
24
|
+
@mutex.synchronize do
|
|
25
|
+
t = Thread.current
|
|
26
|
+
@waiting_threads.push(t)
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
Thread.stop
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def notify
|
|
33
|
+
@mutex.synchronize do
|
|
34
|
+
t = @waiting_threads.shift
|
|
35
|
+
begin
|
|
36
|
+
t.run if t
|
|
37
|
+
rescue ThreadError
|
|
38
|
+
retry
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def notify_all
|
|
44
|
+
@mutex.synchronize do
|
|
45
|
+
@waiting_threads.each do |t|
|
|
46
|
+
t.run
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
@waiting_threads.clear
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def waiting_set_size
|
|
54
|
+
@mutex.synchronize { @waiting_threads.size }
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def any_threads_waiting?
|
|
58
|
+
@mutex.synchronize { !@waiting_threads.empty? }
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def none_threads_waiting?
|
|
62
|
+
@mutex.synchronize { @waiting_threads.empty? }
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
end
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
require "thread"
|
|
2
|
+
|
|
3
|
+
module Bunny
|
|
4
|
+
module Concurrent
|
|
5
|
+
# Continuation queue implementation for MRI and Rubinius
|
|
6
|
+
#
|
|
7
|
+
# @private
|
|
8
|
+
class ContinuationQueue
|
|
9
|
+
def initialize
|
|
10
|
+
@q = []
|
|
11
|
+
@lock = ::Mutex.new
|
|
12
|
+
@cond = ::ConditionVariable.new
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def push(item)
|
|
16
|
+
@lock.synchronize do
|
|
17
|
+
@q.push(item)
|
|
18
|
+
@cond.signal
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
alias << push
|
|
22
|
+
|
|
23
|
+
def pop
|
|
24
|
+
poll
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def poll(timeout_in_ms = nil)
|
|
28
|
+
timeout = timeout_in_ms ? timeout_in_ms / 1000.0 : nil
|
|
29
|
+
|
|
30
|
+
@lock.synchronize do
|
|
31
|
+
timeout_strikes_at = Time.now.utc + (timeout || 0)
|
|
32
|
+
while @q.empty?
|
|
33
|
+
wait = if timeout
|
|
34
|
+
timeout_strikes_at - Time.now.utc
|
|
35
|
+
else
|
|
36
|
+
nil
|
|
37
|
+
end
|
|
38
|
+
@cond.wait(@lock, wait)
|
|
39
|
+
raise ::Timeout::Error if wait && Time.now.utc >= timeout_strikes_at
|
|
40
|
+
end
|
|
41
|
+
item = @q.shift
|
|
42
|
+
item
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def clear
|
|
47
|
+
@lock.synchronize do
|
|
48
|
+
@q.clear
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def empty?
|
|
53
|
+
@q.empty?
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
def size
|
|
57
|
+
@q.size
|
|
58
|
+
end
|
|
59
|
+
alias length size
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
end
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
if !defined?(JRUBY_VERSION)
|
|
2
|
+
raise "Bunny::Concurrent::LinkedContinuationQueue can only be used on JRuby!"
|
|
3
|
+
end
|
|
4
|
+
|
|
5
|
+
require "java"
|
|
6
|
+
|
|
7
|
+
java_import java.util.concurrent.LinkedBlockingQueue
|
|
8
|
+
java_import java.util.concurrent.TimeUnit
|
|
9
|
+
|
|
10
|
+
module Bunny
|
|
11
|
+
module Concurrent
|
|
12
|
+
# Continuation queue implementation for JRuby.
|
|
13
|
+
#
|
|
14
|
+
# On JRuby, we'd rather use reliable and heavily battle tested j.u.c.
|
|
15
|
+
# primitives with well described semantics than informally specified, clumsy
|
|
16
|
+
# and limited Ruby standard library parts.
|
|
17
|
+
#
|
|
18
|
+
# This is an implementation of the continuation queue on top of the linked blocking
|
|
19
|
+
# queue in j.u.c.
|
|
20
|
+
#
|
|
21
|
+
# Compared to the Ruby standard library Queue, there is one limitation: you cannot
|
|
22
|
+
# push a nil on the queue, it will fail with a null pointer exception.
|
|
23
|
+
# @private
|
|
24
|
+
class LinkedContinuationQueue
|
|
25
|
+
def initialize(*args, &block)
|
|
26
|
+
@q = LinkedBlockingQueue.new
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def push(el, timeout_in_ms = nil)
|
|
30
|
+
if timeout_in_ms
|
|
31
|
+
@q.offer(el, timeout_in_ms, TimeUnit::MILLISECONDS)
|
|
32
|
+
else
|
|
33
|
+
@q.offer(el)
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
alias << push
|
|
37
|
+
|
|
38
|
+
def pop
|
|
39
|
+
@q.take
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def poll(timeout_in_ms = nil)
|
|
43
|
+
if timeout_in_ms
|
|
44
|
+
v = @q.poll(timeout_in_ms, TimeUnit::MILLISECONDS)
|
|
45
|
+
raise ::Timeout::Error.new("operation did not finish in #{timeout_in_ms} ms") if v.nil?
|
|
46
|
+
v
|
|
47
|
+
else
|
|
48
|
+
@q.poll
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def clear
|
|
53
|
+
@q.clear
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
def method_missing(selector, *args, &block)
|
|
57
|
+
@q.__send__(selector, *args, &block)
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
end
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
require "set"
|
|
2
|
+
require "thread"
|
|
3
|
+
|
|
4
|
+
module Bunny
|
|
5
|
+
module Concurrent
|
|
6
|
+
# A SortedSet variation that synchronizes key mutation operations.
|
|
7
|
+
#
|
|
8
|
+
# @note This is NOT a complete SortedSet replacement. It only synchronizes operations needed by Bunny.
|
|
9
|
+
# @api public
|
|
10
|
+
class SynchronizedSortedSet < SortedSet
|
|
11
|
+
def initialize(enum = nil)
|
|
12
|
+
@mutex = Mutex.new
|
|
13
|
+
|
|
14
|
+
super
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def add(o)
|
|
18
|
+
# avoid using Mutex#synchronize because of a Ruby 1.8.7-specific
|
|
19
|
+
# bug that prevents super from being called from within a block. MK.
|
|
20
|
+
@mutex.lock
|
|
21
|
+
begin
|
|
22
|
+
super
|
|
23
|
+
ensure
|
|
24
|
+
@mutex.unlock
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def delete(o)
|
|
29
|
+
@mutex.lock
|
|
30
|
+
begin
|
|
31
|
+
super
|
|
32
|
+
ensure
|
|
33
|
+
@mutex.unlock
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def delete_if(&block)
|
|
38
|
+
@mutex.lock
|
|
39
|
+
begin
|
|
40
|
+
super
|
|
41
|
+
ensure
|
|
42
|
+
@mutex.unlock
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def include?(o)
|
|
47
|
+
@mutex.lock
|
|
48
|
+
begin
|
|
49
|
+
super
|
|
50
|
+
ensure
|
|
51
|
+
@mutex.unlock
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
end
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
module Bunny
|
|
2
|
+
# Base class that represents consumer interface. Subclasses of this class implement
|
|
3
|
+
# specific logic of handling consumer life cycle events. Note that when the only event
|
|
4
|
+
# you are interested in is message deliveries, it is recommended to just use
|
|
5
|
+
# {Bunny::Queue#subscribe} instead of subclassing this class.
|
|
6
|
+
#
|
|
7
|
+
# @see Bunny::Queue#subscribe
|
|
8
|
+
# @see Bunny::Queue#subscribe_with
|
|
9
|
+
# @see http://rubybunny.info/articles/queues.html Queues and Consumers guide
|
|
10
|
+
# @api public
|
|
11
|
+
class Consumer
|
|
12
|
+
|
|
13
|
+
#
|
|
14
|
+
# API
|
|
15
|
+
#
|
|
16
|
+
|
|
17
|
+
attr_reader :channel
|
|
18
|
+
attr_reader :queue
|
|
19
|
+
attr_accessor :consumer_tag
|
|
20
|
+
attr_reader :arguments
|
|
21
|
+
attr_reader :no_ack
|
|
22
|
+
attr_reader :exclusive
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
# @param [Bunny::Channel] channel Channel this consumer will use
|
|
26
|
+
# @param [Bunny::Queue,String] queue Queue messages will be consumed from
|
|
27
|
+
# @param [String] consumer_tag Consumer tag (unique identifier). Generally it is better to let Bunny generate one.
|
|
28
|
+
# Empty string means RabbitMQ will generate consumer tag.
|
|
29
|
+
# @param [Boolean] no_ack (true) If true, delivered messages will be automatically acknowledged.
|
|
30
|
+
# If false, manual acknowledgements will be necessary.
|
|
31
|
+
# @param [Boolean] exclusive (false) Should this consumer be exclusive?
|
|
32
|
+
# @param [Hash] arguments (nil) Optional arguments that may be used by RabbitMQ extensions, etc
|
|
33
|
+
# @api public
|
|
34
|
+
def initialize(channel, queue, consumer_tag = channel.generate_consumer_tag, no_ack = true, exclusive = false, arguments = {})
|
|
35
|
+
@channel = channel || raise(ArgumentError, "channel is nil")
|
|
36
|
+
@queue = queue || raise(ArgumentError, "queue is nil")
|
|
37
|
+
@consumer_tag = consumer_tag
|
|
38
|
+
@exclusive = exclusive
|
|
39
|
+
@arguments = arguments
|
|
40
|
+
# no_ack set to true = no manual ack = automatic ack. MK.
|
|
41
|
+
@no_ack = no_ack
|
|
42
|
+
|
|
43
|
+
@on_cancellation = []
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
# Defines message delivery handler
|
|
47
|
+
# @api public
|
|
48
|
+
def on_delivery(&block)
|
|
49
|
+
@on_delivery = block
|
|
50
|
+
self
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
# Invokes message delivery handler
|
|
54
|
+
# @private
|
|
55
|
+
def call(*args)
|
|
56
|
+
@on_delivery.call(*args) if @on_delivery
|
|
57
|
+
end
|
|
58
|
+
alias handle_delivery call
|
|
59
|
+
|
|
60
|
+
# Defines consumer cancellation notification handler
|
|
61
|
+
#
|
|
62
|
+
# @see http://rubybunny.info/articles/queues.html Queues and Consumers guide
|
|
63
|
+
# @see http://rubybunny.info/articles/extensions.html RabbitMQ Extensions guide
|
|
64
|
+
# @api public
|
|
65
|
+
def on_cancellation(&block)
|
|
66
|
+
@on_cancellation << block
|
|
67
|
+
self
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
# Invokes consumer cancellation notification handler
|
|
71
|
+
# @private
|
|
72
|
+
def handle_cancellation(basic_cancel)
|
|
73
|
+
@on_cancellation.each do |fn|
|
|
74
|
+
fn.call(basic_cancel)
|
|
75
|
+
end
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
# Cancels this consumer. Messages for this consumer will no longer be delivered. If the queue
|
|
79
|
+
# it was on is auto-deleted and this consumer was the last one, the queue will be deleted.
|
|
80
|
+
#
|
|
81
|
+
# @see http://rubybunny.info/articles/queues.html Queues and Consumers guide
|
|
82
|
+
# @api public
|
|
83
|
+
def cancel
|
|
84
|
+
@channel.basic_cancel(@consumer_tag)
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
# @return [String] More detailed human-readable string representation of this consumer
|
|
88
|
+
def inspect
|
|
89
|
+
"#<#{self.class.name}:#{object_id} @channel_id=#{@channel.number} @queue=#{self.queue_name}> @consumer_tag=#{@consumer_tag} @exclusive=#{@exclusive} @no_ack=#{@no_ack}>"
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
# @return [String] Brief human-readable string representation of this consumer
|
|
93
|
+
def to_s
|
|
94
|
+
"#<#{self.class.name}:#{object_id} @channel_id=#{@channel.number} @queue=#{self.queue_name}> @consumer_tag=#{@consumer_tag}>"
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
# @return [Boolean] true if this consumer uses automatic acknowledgement mode
|
|
98
|
+
# @api public
|
|
99
|
+
def automatic_acknowledgement?
|
|
100
|
+
@no_ack == true
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
# @return [Boolean] true if this consumer uses manual (explicit) acknowledgement mode
|
|
104
|
+
# @api public
|
|
105
|
+
def manual_acknowledgement?
|
|
106
|
+
@no_ack == false
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
# @return [String] Name of the queue this consumer is on
|
|
110
|
+
# @api public
|
|
111
|
+
def queue_name
|
|
112
|
+
if @queue.respond_to?(:name)
|
|
113
|
+
@queue.name
|
|
114
|
+
else
|
|
115
|
+
@queue
|
|
116
|
+
end
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
#
|
|
120
|
+
# Recovery
|
|
121
|
+
#
|
|
122
|
+
|
|
123
|
+
# @private
|
|
124
|
+
def recover_from_network_failure
|
|
125
|
+
@channel.basic_consume_with(self)
|
|
126
|
+
end
|
|
127
|
+
end
|
|
128
|
+
end
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
module Bunny
|
|
2
|
+
# Used to generate consumer tags in the client
|
|
3
|
+
class ConsumerTagGenerator
|
|
4
|
+
|
|
5
|
+
#
|
|
6
|
+
# API
|
|
7
|
+
#
|
|
8
|
+
|
|
9
|
+
# @return [String] Generated consumer tag
|
|
10
|
+
def generate
|
|
11
|
+
"#{Kernel.rand}-#{Time.now.to_i * 1000}-#{Kernel.rand(999_999_999_999)}"
|
|
12
|
+
end # generate
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
# Unique string supposed to be used as a consumer tag.
|
|
16
|
+
#
|
|
17
|
+
# @return [String] Unique string.
|
|
18
|
+
# @api public
|
|
19
|
+
def generate_prefixed(name = "bunny")
|
|
20
|
+
"#{name}-#{Time.now.to_i * 1000}-#{Kernel.rand(999_999_999_999)}"
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
require "thread"
|
|
2
|
+
|
|
3
|
+
module Bunny
|
|
4
|
+
# Thread pool that dispatches consumer deliveries. Not supposed to be shared between channels
|
|
5
|
+
# or threads.
|
|
6
|
+
#
|
|
7
|
+
# Every channel its own consumer pool.
|
|
8
|
+
#
|
|
9
|
+
# @private
|
|
10
|
+
class ConsumerWorkPool
|
|
11
|
+
|
|
12
|
+
#
|
|
13
|
+
# API
|
|
14
|
+
#
|
|
15
|
+
|
|
16
|
+
attr_reader :threads
|
|
17
|
+
attr_reader :size
|
|
18
|
+
attr_reader :abort_on_exception
|
|
19
|
+
|
|
20
|
+
def initialize(size = 1, abort_on_exception = false, shutdown_timeout = 60)
|
|
21
|
+
@size = size
|
|
22
|
+
@abort_on_exception = abort_on_exception
|
|
23
|
+
@shutdown_timeout = shutdown_timeout
|
|
24
|
+
@shutdown_mutex = ::Mutex.new
|
|
25
|
+
@shutdown_conditional = ::ConditionVariable.new
|
|
26
|
+
@queue = ::Queue.new
|
|
27
|
+
@paused = false
|
|
28
|
+
@running = false
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
def submit(callable = nil, &block)
|
|
33
|
+
@queue.push(callable || block)
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def start
|
|
37
|
+
@threads = []
|
|
38
|
+
|
|
39
|
+
@size.times do
|
|
40
|
+
t = Thread.new(&method(:run_loop))
|
|
41
|
+
t.abort_on_exception = true if abort_on_exception
|
|
42
|
+
@threads << t
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
@running = true
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def running?
|
|
49
|
+
@running
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def backlog
|
|
53
|
+
@queue.length
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
def busy?
|
|
57
|
+
!@queue.empty?
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
def shutdown(wait_for_workers = false)
|
|
61
|
+
was_running = running?
|
|
62
|
+
@running = false
|
|
63
|
+
|
|
64
|
+
@size.times do
|
|
65
|
+
submit do |*args|
|
|
66
|
+
throw :terminate
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
return if !(wait_for_workers && @shutdown_timeout && was_running)
|
|
71
|
+
|
|
72
|
+
@shutdown_mutex.synchronize do
|
|
73
|
+
@shutdown_conditional.wait(@shutdown_mutex, @shutdown_timeout)
|
|
74
|
+
end
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
def join(timeout = nil)
|
|
78
|
+
(@threads || []).each { |t| t.join(timeout) }
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
def pause
|
|
82
|
+
@running = false
|
|
83
|
+
@paused = true
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
def resume
|
|
87
|
+
@running = true
|
|
88
|
+
@paused = false
|
|
89
|
+
|
|
90
|
+
@threads.each { |t| t.run }
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
def kill
|
|
94
|
+
@running = false
|
|
95
|
+
|
|
96
|
+
(@threads || []).each { |t| t.kill }
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
protected
|
|
100
|
+
|
|
101
|
+
def run_loop
|
|
102
|
+
catch(:terminate) do
|
|
103
|
+
loop do
|
|
104
|
+
Thread.stop if @paused
|
|
105
|
+
callable = @queue.pop
|
|
106
|
+
|
|
107
|
+
begin
|
|
108
|
+
callable.call
|
|
109
|
+
rescue ::StandardError => e
|
|
110
|
+
# TODO: use connection logger
|
|
111
|
+
$stderr.puts e.class.name
|
|
112
|
+
$stderr.puts e.message
|
|
113
|
+
end
|
|
114
|
+
end
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
@shutdown_mutex.synchronize do
|
|
118
|
+
@shutdown_conditional.signal unless busy?
|
|
119
|
+
end
|
|
120
|
+
end
|
|
121
|
+
end
|
|
122
|
+
end
|