bunny 1.3.0 → 2.17.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +5 -5
- data/.github/ISSUE_TEMPLATE.md +18 -0
- data/.gitignore +7 -1
- data/.rspec +1 -3
- data/.travis.yml +21 -14
- data/CONTRIBUTING.md +132 -0
- data/ChangeLog.md +887 -1
- data/Gemfile +13 -13
- data/LICENSE +1 -1
- data/README.md +46 -60
- data/Rakefile +54 -0
- data/bunny.gemspec +5 -11
- data/docker-compose.yml +28 -0
- data/docker/Dockerfile +24 -0
- data/docker/apt/preferences.d/erlang +3 -0
- data/docker/apt/sources.list.d/bintray.rabbitmq.list +2 -0
- data/docker/docker-entrypoint.sh +26 -0
- data/docker/rabbitmq.conf +29 -0
- data/examples/connection/automatic_recovery_with_basic_get.rb +1 -1
- data/examples/connection/automatic_recovery_with_client_named_queues.rb +1 -1
- data/examples/connection/automatic_recovery_with_multiple_consumers.rb +1 -1
- data/examples/connection/automatic_recovery_with_republishing.rb +1 -1
- data/examples/connection/automatic_recovery_with_server_named_queues.rb +1 -1
- data/examples/connection/channel_level_exception.rb +1 -9
- data/examples/connection/disabled_automatic_recovery.rb +1 -1
- data/examples/connection/heartbeat.rb +1 -1
- data/examples/consumers/high_and_low_priority.rb +1 -1
- data/examples/guides/extensions/alternate_exchange.rb +2 -0
- data/examples/guides/extensions/basic_nack.rb +1 -1
- data/examples/guides/extensions/dead_letter_exchange.rb +1 -1
- data/examples/guides/getting_started/hello_world.rb +2 -0
- data/examples/guides/getting_started/weathr.rb +2 -0
- data/examples/guides/queues/one_off_consumer.rb +2 -0
- data/examples/guides/queues/redeliveries.rb +4 -2
- data/lib/bunny.rb +8 -4
- data/lib/bunny/channel.rb +268 -153
- data/lib/bunny/channel_id_allocator.rb +6 -4
- data/lib/bunny/concurrent/continuation_queue.rb +34 -13
- data/lib/bunny/consumer_work_pool.rb +34 -6
- data/lib/bunny/cruby/socket.rb +48 -21
- data/lib/bunny/cruby/ssl_socket.rb +65 -4
- data/lib/bunny/exceptions.rb +25 -4
- data/lib/bunny/exchange.rb +24 -28
- data/lib/bunny/get_response.rb +1 -1
- data/lib/bunny/heartbeat_sender.rb +3 -2
- data/lib/bunny/jruby/socket.rb +23 -6
- data/lib/bunny/jruby/ssl_socket.rb +5 -0
- data/lib/bunny/queue.rb +31 -22
- data/lib/bunny/reader_loop.rb +31 -18
- data/lib/bunny/session.rb +448 -159
- data/lib/bunny/test_kit.rb +14 -0
- data/lib/bunny/timeout.rb +1 -12
- data/lib/bunny/transport.rb +205 -98
- data/lib/bunny/version.rb +1 -1
- data/repl +1 -1
- data/spec/config/enabled_plugins +1 -0
- data/spec/config/rabbitmq.conf +13 -0
- data/spec/higher_level_api/integration/basic_ack_spec.rb +175 -16
- data/spec/higher_level_api/integration/basic_cancel_spec.rb +77 -11
- data/spec/higher_level_api/integration/basic_consume_spec.rb +60 -55
- data/spec/higher_level_api/integration/basic_consume_with_objects_spec.rb +6 -6
- data/spec/higher_level_api/integration/basic_get_spec.rb +31 -7
- data/spec/higher_level_api/integration/basic_nack_spec.rb +22 -19
- data/spec/higher_level_api/integration/basic_publish_spec.rb +11 -100
- data/spec/higher_level_api/integration/basic_qos_spec.rb +32 -4
- data/spec/higher_level_api/integration/basic_reject_spec.rb +94 -16
- data/spec/higher_level_api/integration/basic_return_spec.rb +4 -4
- data/spec/higher_level_api/integration/channel_close_spec.rb +51 -10
- data/spec/higher_level_api/integration/channel_open_spec.rb +12 -12
- data/spec/higher_level_api/integration/connection_recovery_spec.rb +424 -221
- data/spec/higher_level_api/integration/connection_spec.rb +300 -126
- data/spec/higher_level_api/integration/connection_stop_spec.rb +31 -19
- data/spec/higher_level_api/integration/consumer_cancellation_notification_spec.rb +17 -17
- data/spec/higher_level_api/integration/dead_lettering_spec.rb +34 -11
- data/spec/higher_level_api/integration/exchange_bind_spec.rb +5 -5
- data/spec/higher_level_api/integration/exchange_declare_spec.rb +32 -31
- data/spec/higher_level_api/integration/exchange_delete_spec.rb +12 -12
- data/spec/higher_level_api/integration/exchange_unbind_spec.rb +5 -5
- data/spec/higher_level_api/integration/exclusive_queue_spec.rb +5 -5
- data/spec/higher_level_api/integration/heartbeat_spec.rb +26 -8
- data/spec/higher_level_api/integration/message_properties_access_spec.rb +49 -49
- data/spec/higher_level_api/integration/predeclared_exchanges_spec.rb +2 -2
- data/spec/higher_level_api/integration/publisher_confirms_spec.rb +156 -42
- data/spec/higher_level_api/integration/publishing_edge_cases_spec.rb +19 -19
- data/spec/higher_level_api/integration/queue_bind_spec.rb +23 -23
- data/spec/higher_level_api/integration/queue_declare_spec.rb +129 -34
- data/spec/higher_level_api/integration/queue_delete_spec.rb +2 -2
- data/spec/higher_level_api/integration/queue_purge_spec.rb +5 -5
- data/spec/higher_level_api/integration/queue_unbind_spec.rb +6 -6
- data/spec/higher_level_api/integration/read_only_consumer_spec.rb +9 -9
- data/spec/higher_level_api/integration/sender_selected_distribution_spec.rb +10 -10
- data/spec/higher_level_api/integration/tls_connection_spec.rb +224 -89
- data/spec/higher_level_api/integration/toxiproxy_spec.rb +76 -0
- data/spec/higher_level_api/integration/tx_commit_spec.rb +1 -1
- data/spec/higher_level_api/integration/tx_rollback_spec.rb +1 -1
- data/spec/higher_level_api/integration/with_channel_spec.rb +2 -2
- data/spec/issues/issue100_spec.rb +11 -11
- data/spec/issues/issue141_spec.rb +13 -14
- data/spec/issues/issue202_spec.rb +1 -1
- data/spec/issues/issue224_spec.rb +40 -0
- data/spec/issues/issue465_spec.rb +32 -0
- data/spec/issues/issue549_spec.rb +30 -0
- data/spec/issues/issue78_spec.rb +21 -24
- data/spec/issues/issue83_spec.rb +5 -6
- data/spec/issues/issue97_spec.rb +44 -45
- data/spec/lower_level_api/integration/basic_cancel_spec.rb +15 -16
- data/spec/lower_level_api/integration/basic_consume_spec.rb +20 -21
- data/spec/spec_helper.rb +8 -26
- data/spec/stress/channel_close_stress_spec.rb +64 -0
- data/spec/stress/channel_open_stress_spec.rb +15 -9
- data/spec/stress/channel_open_stress_with_single_threaded_connection_spec.rb +7 -7
- data/spec/stress/concurrent_consumers_stress_spec.rb +18 -16
- data/spec/stress/concurrent_publishers_stress_spec.rb +16 -19
- data/spec/stress/connection_open_close_spec.rb +9 -9
- data/spec/stress/merry_go_round_spec.rb +105 -0
- data/spec/tls/client_key.pem +49 -25
- data/spec/tls/generate-server-cert.sh +8 -0
- data/spec/tls/server-openssl.cnf +10 -0
- data/spec/tls/server.csr +16 -0
- data/spec/tls/server_key.pem +49 -25
- data/spec/toxiproxy_helper.rb +28 -0
- data/spec/unit/bunny_spec.rb +5 -5
- data/spec/unit/concurrent/atomic_fixnum_spec.rb +6 -6
- data/spec/unit/concurrent/condition_spec.rb +8 -8
- data/spec/unit/concurrent/linked_continuation_queue_spec.rb +2 -2
- data/spec/unit/concurrent/synchronized_sorted_set_spec.rb +16 -16
- data/spec/unit/exchange_recovery_spec.rb +39 -0
- data/spec/unit/version_delivery_tag_spec.rb +3 -3
- metadata +65 -47
- data/.ruby-version +0 -1
- data/lib/bunny/compatibility.rb +0 -24
- data/lib/bunny/system_timer.rb +0 -20
- data/spec/compatibility/queue_declare_spec.rb +0 -44
- data/spec/compatibility/queue_declare_with_default_channel_spec.rb +0 -33
- data/spec/higher_level_api/integration/basic_recover_spec.rb +0 -18
- data/spec/higher_level_api/integration/confirm_select_spec.rb +0 -19
- data/spec/higher_level_api/integration/consistent_hash_exchange_spec.rb +0 -50
- data/spec/higher_level_api/integration/merry_go_round_spec.rb +0 -85
- data/spec/stress/long_running_consumer_spec.rb +0 -83
- data/spec/tls/cacert.pem +0 -18
- data/spec/tls/client_cert.pem +0 -18
- data/spec/tls/server_cert.pem +0 -18
- data/spec/unit/system_timer_spec.rb +0 -10
data/lib/bunny/exchange.rb
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
require
|
1
|
+
require 'amq/protocol'
|
2
2
|
|
3
3
|
module Bunny
|
4
4
|
# Represents AMQP 0.9.1 exchanges.
|
@@ -7,9 +7,6 @@ module Bunny
|
|
7
7
|
# @see http://rubybunny.info/articles/extensions.html RabbitMQ Extensions guide
|
8
8
|
class Exchange
|
9
9
|
|
10
|
-
include Bunny::Compatibility
|
11
|
-
|
12
|
-
|
13
10
|
#
|
14
11
|
# API
|
15
12
|
#
|
@@ -33,12 +30,12 @@ module Bunny
|
|
33
30
|
attr_accessor :opts
|
34
31
|
|
35
32
|
|
36
|
-
# The default exchange.
|
37
|
-
#
|
38
|
-
# the following routing semantics: messages will be routed to the queue
|
39
|
-
#
|
40
|
-
# a routing key of "weather.usa.ca.sandiego" and there is a queue
|
41
|
-
#
|
33
|
+
# The default exchange. This exchange is a direct exchange that is predefined by the broker
|
34
|
+
# and that cannot be removed. Every queue is bound to this exchange by default with
|
35
|
+
# the following routing semantics: messages will be routed to the queue with the same
|
36
|
+
# name as the message's routing key. In other words, if a message is published with
|
37
|
+
# a routing key of "weather.usa.ca.sandiego" and there is a queue with this name,
|
38
|
+
# the message will be routed to the queue.
|
42
39
|
#
|
43
40
|
# @param [Bunny::Channel] channel_or_connection Channel to use. {Bunny::Session} instances
|
44
41
|
# are only supported for backwards compatibility.
|
@@ -46,11 +43,11 @@ module Bunny
|
|
46
43
|
# @example Publishing a messages to the tasks queue
|
47
44
|
# channel = Bunny::Channel.new(connection)
|
48
45
|
# tasks_queue = channel.queue("tasks")
|
49
|
-
# Bunny::Exchange.default(channel).publish("make clean", routing_key => "tasks")
|
46
|
+
# Bunny::Exchange.default(channel).publish("make clean", :routing_key => "tasks")
|
50
47
|
#
|
51
48
|
# @see http://rubybunny.info/articles/exchanges.html Exchanges and Publishing guide
|
52
49
|
# @see http://www.rabbitmq.com/resources/specs/amqp0-9-1.pdf AMQP 0.9.1 specification (Section 2.1.2.4)
|
53
|
-
# @note Do not confuse default exchange with amq.direct: amq.direct is a pre-defined direct
|
50
|
+
# @note Do not confuse the default exchange with amq.direct: amq.direct is a pre-defined direct
|
54
51
|
# exchange that doesn't have any special routing semantics.
|
55
52
|
# @return [Exchange] An instance that corresponds to the default exchange (of type direct).
|
56
53
|
# @api public
|
@@ -58,11 +55,10 @@ module Bunny
|
|
58
55
|
self.new(channel_or_connection, :direct, AMQ::Protocol::EMPTY_STRING, :no_declare => true)
|
59
56
|
end
|
60
57
|
|
61
|
-
# @param [Bunny::Channel]
|
62
|
-
#
|
63
|
-
# @param [
|
64
|
-
# @param [
|
65
|
-
# @param [Hash] opts Exchange properties
|
58
|
+
# @param [Bunny::Channel] channel Channel this exchange will use.
|
59
|
+
# @param [Symbol,String] type Exchange type
|
60
|
+
# @param [String] name Exchange name
|
61
|
+
# @param [Hash] opts Exchange properties
|
66
62
|
#
|
67
63
|
# @option opts [Boolean] :durable (false) Should this exchange be durable?
|
68
64
|
# @option opts [Boolean] :auto_delete (false) Should this exchange be automatically deleted when it is no longer used?
|
@@ -75,10 +71,8 @@ module Bunny
|
|
75
71
|
# @see http://rubybunny.info/articles/exchanges.html Exchanges and Publishing guide
|
76
72
|
# @see http://rubybunny.info/articles/extensions.html RabbitMQ Extensions guide
|
77
73
|
# @api public
|
78
|
-
def initialize(
|
79
|
-
|
80
|
-
# we just use default channel from it. MK.
|
81
|
-
@channel = channel_from(channel_or_connection)
|
74
|
+
def initialize(channel, type, name, opts = {})
|
75
|
+
@channel = channel
|
82
76
|
@name = name
|
83
77
|
@type = type
|
84
78
|
@options = self.class.add_default_options(name, opts)
|
@@ -88,6 +82,8 @@ module Bunny
|
|
88
82
|
@internal = @options[:internal]
|
89
83
|
@arguments = @options[:arguments]
|
90
84
|
|
85
|
+
@bindings = Set.new
|
86
|
+
|
91
87
|
declare! unless opts[:no_declare] || predeclared? || (@name == AMQ::Protocol::EMPTY_STRING)
|
92
88
|
|
93
89
|
@channel.register_exchange(self)
|
@@ -177,6 +173,7 @@ module Bunny
|
|
177
173
|
# @api public
|
178
174
|
def bind(source, opts = {})
|
179
175
|
@channel.exchange_bind(source, self, opts)
|
176
|
+
@bindings.add(source: source, opts: opts)
|
180
177
|
|
181
178
|
self
|
182
179
|
end
|
@@ -197,6 +194,7 @@ module Bunny
|
|
197
194
|
# @api public
|
198
195
|
def unbind(source, opts = {})
|
199
196
|
@channel.exchange_unbind(source, self, opts)
|
197
|
+
@bindings.delete(source: source, opts: opts)
|
200
198
|
|
201
199
|
self
|
202
200
|
end
|
@@ -222,8 +220,11 @@ module Bunny
|
|
222
220
|
|
223
221
|
# @private
|
224
222
|
def recover_from_network_failure
|
225
|
-
|
226
|
-
|
223
|
+
declare! unless @options[:no_declare] ||predefined?
|
224
|
+
|
225
|
+
@bindings.each do |b|
|
226
|
+
bind(b[:source], b[:opts])
|
227
|
+
end
|
227
228
|
end
|
228
229
|
|
229
230
|
|
@@ -253,11 +254,6 @@ module Bunny
|
|
253
254
|
@channel.exchange_declare(@name, @type, @options)
|
254
255
|
end
|
255
256
|
|
256
|
-
# @private
|
257
|
-
def self.add_default_options(name, opts, block)
|
258
|
-
{ :exchange => name, :nowait => (block.nil? && !name.empty?) }.merge(opts)
|
259
|
-
end
|
260
|
-
|
261
257
|
# @private
|
262
258
|
def self.add_default_options(name, opts)
|
263
259
|
# :nowait is always false for Bunny
|
data/lib/bunny/get_response.rb
CHANGED
@@ -29,6 +29,7 @@ module Bunny
|
|
29
29
|
@interval = [(period / 2) - 1, 0.4].max
|
30
30
|
|
31
31
|
@thread = Thread.new(&method(:run))
|
32
|
+
@thread.report_on_exception = false if @thread.respond_to?(:report_on_exception)
|
32
33
|
end
|
33
34
|
end
|
34
35
|
|
@@ -62,8 +63,8 @@ module Bunny
|
|
62
63
|
now = Time.now
|
63
64
|
|
64
65
|
if now > (@last_activity_time + @interval)
|
65
|
-
@logger.debug "Sending a heartbeat, last activity time: #{@last_activity_time}, interval (s): #{@interval}"
|
66
|
-
@transport.write_without_timeout(AMQ::Protocol::HeartbeatFrame.encode)
|
66
|
+
@logger.debug { "Sending a heartbeat, last activity time: #{@last_activity_time}, interval (s): #{@interval}" }
|
67
|
+
@transport.write_without_timeout(AMQ::Protocol::HeartbeatFrame.encode, true)
|
67
68
|
end
|
68
69
|
end
|
69
70
|
end
|
data/lib/bunny/jruby/socket.rb
CHANGED
@@ -5,7 +5,22 @@ module Bunny
|
|
5
5
|
# TCP socket extension that uses Socket#readpartial to avoid excessive CPU
|
6
6
|
# burn after some time. See issue #165.
|
7
7
|
# @private
|
8
|
-
|
8
|
+
module Socket
|
9
|
+
include Bunny::Socket
|
10
|
+
|
11
|
+
def self.open(host, port, options = {})
|
12
|
+
socket = ::Socket.tcp(host, port, nil, nil,
|
13
|
+
connect_timeout: options[:connect_timeout])
|
14
|
+
if ::Socket.constants.include?('TCP_NODELAY') || ::Socket.constants.include?(:TCP_NODELAY)
|
15
|
+
socket.setsockopt(::Socket::IPPROTO_TCP, ::Socket::TCP_NODELAY, true)
|
16
|
+
end
|
17
|
+
socket.setsockopt(::Socket::SOL_SOCKET, ::Socket::SO_KEEPALIVE, true) if options.fetch(:keepalive, true)
|
18
|
+
socket.extend self
|
19
|
+
socket.options = { :host => host, :port => port }.merge(options)
|
20
|
+
socket
|
21
|
+
rescue Errno::ETIMEDOUT
|
22
|
+
raise ClientTimeout
|
23
|
+
end
|
9
24
|
|
10
25
|
# Reads given number of bytes with an optional timeout
|
11
26
|
#
|
@@ -15,17 +30,17 @@ module Bunny
|
|
15
30
|
# @return [String] Data read from the socket
|
16
31
|
# @api public
|
17
32
|
def read_fully(count, timeout = nil)
|
18
|
-
return nil if @__bunny_socket_eof_flag__
|
19
|
-
|
20
33
|
value = ''
|
34
|
+
|
21
35
|
begin
|
22
36
|
loop do
|
23
|
-
value <<
|
37
|
+
value << read_nonblock(count - value.bytesize)
|
24
38
|
break if value.bytesize >= count
|
25
39
|
end
|
26
40
|
rescue EOFError
|
27
|
-
#
|
28
|
-
|
41
|
+
# JRuby specific fix via https://github.com/jruby/jruby/issues/1694#issuecomment-54873532
|
42
|
+
IO.select([self], nil, nil, timeout)
|
43
|
+
retry
|
29
44
|
rescue *READ_RETRY_EXCEPTION_CLASSES
|
30
45
|
if IO.select([self], nil, nil, timeout)
|
31
46
|
retry
|
@@ -33,8 +48,10 @@ module Bunny
|
|
33
48
|
raise Timeout::Error, "IO timeout when reading #{count} bytes"
|
34
49
|
end
|
35
50
|
end
|
51
|
+
|
36
52
|
value
|
37
53
|
end # read_fully
|
54
|
+
|
38
55
|
end
|
39
56
|
end
|
40
57
|
end
|
@@ -8,6 +8,11 @@ module Bunny
|
|
8
8
|
# methods found in Bunny::Socket.
|
9
9
|
class SSLSocket < Bunny::SSLSocket
|
10
10
|
|
11
|
+
def initialize(*args)
|
12
|
+
super
|
13
|
+
@__bunny_socket_eof_flag__ = false
|
14
|
+
end
|
15
|
+
|
11
16
|
# Reads given number of bytes with an optional timeout
|
12
17
|
#
|
13
18
|
# @param [Integer] count How many bytes to read
|
data/lib/bunny/queue.rb
CHANGED
@@ -1,4 +1,3 @@
|
|
1
|
-
require "bunny/compatibility"
|
2
1
|
require "bunny/get_response"
|
3
2
|
|
4
3
|
module Bunny
|
@@ -8,9 +7,6 @@ module Bunny
|
|
8
7
|
# @see http://rubybunny.info/articles/extensions.html RabbitMQ Extensions guide
|
9
8
|
class Queue
|
10
9
|
|
11
|
-
include Bunny::Compatibility
|
12
|
-
|
13
|
-
|
14
10
|
#
|
15
11
|
# API
|
16
12
|
#
|
@@ -22,8 +18,7 @@ module Bunny
|
|
22
18
|
# @return [Hash] Options this queue was created with
|
23
19
|
attr_reader :options
|
24
20
|
|
25
|
-
# @param [Bunny::Channel]
|
26
|
-
# backwards compatibility with 0.8.
|
21
|
+
# @param [Bunny::Channel] channel Channel this queue will use.
|
27
22
|
# @param [String] name Queue name. Pass an empty string to make RabbitMQ generate a unique one.
|
28
23
|
# @param [Hash] opts Queue properties
|
29
24
|
#
|
@@ -36,13 +31,12 @@ module Bunny
|
|
36
31
|
# @see http://rubybunny.info/articles/queues.html Queues and Consumers guide
|
37
32
|
# @see http://rubybunny.info/articles/extensions.html RabbitMQ Extensions guide
|
38
33
|
# @api public
|
39
|
-
def initialize(
|
34
|
+
def initialize(channel, name = AMQ::Protocol::EMPTY_STRING, opts = {})
|
40
35
|
# old Bunny versions pass a connection here. In that case,
|
41
36
|
# we just use default channel from it. MK.
|
42
|
-
@channel =
|
37
|
+
@channel = channel
|
43
38
|
@name = name
|
44
39
|
@options = self.class.add_default_options(name, opts)
|
45
|
-
@consumers = Hash.new
|
46
40
|
|
47
41
|
@durable = @options[:durable]
|
48
42
|
@exclusive = @options[:exclusive]
|
@@ -93,6 +87,15 @@ module Bunny
|
|
93
87
|
@arguments
|
94
88
|
end
|
95
89
|
|
90
|
+
def to_s
|
91
|
+
oid = ("0x%x" % (self.object_id << 1))
|
92
|
+
"<#{self.class.name}:#{oid} @name=\"#{name}\" channel=#{@channel.to_s} @durable=#{@durable} @auto_delete=#{@auto_delete} @exclusive=#{@exclusive} @arguments=#{@arguments}>"
|
93
|
+
end
|
94
|
+
|
95
|
+
def inspect
|
96
|
+
to_s
|
97
|
+
end
|
98
|
+
|
96
99
|
# Binds queue to an exchange
|
97
100
|
#
|
98
101
|
# @param [Bunny::Exchange,String] exchange Exchange to bind to
|
@@ -152,9 +155,9 @@ module Bunny
|
|
152
155
|
#
|
153
156
|
# @param [Hash] opts Options
|
154
157
|
#
|
158
|
+
# @option opts [Boolean] :ack (false) [DEPRECATED] Use :manual_ack instead
|
155
159
|
# @option opts [Boolean] :manual_ack (false) Will this consumer use manual acknowledgements?
|
156
160
|
# @option opts [Boolean] :exclusive (false) Should this consumer be exclusive for this queue?
|
157
|
-
# @option opts [Boolean] :block (false) Should the call block calling thread?
|
158
161
|
# @option opts [#call] :on_cancellation Block to execute when this consumer is cancelled remotely (e.g. via the RabbitMQ Management plugin)
|
159
162
|
# @option opts [String] :consumer_tag Unique consumer identifier. It is usually recommended to let Bunny generate it for you.
|
160
163
|
# @option opts [Hash] :arguments ({}) Additional (optional) arguments, typically used by RabbitMQ extensions
|
@@ -163,17 +166,22 @@ module Bunny
|
|
163
166
|
# @api public
|
164
167
|
def subscribe(opts = {
|
165
168
|
:consumer_tag => @channel.generate_consumer_tag,
|
166
|
-
:
|
169
|
+
:manual_ack => false,
|
167
170
|
:exclusive => false,
|
168
171
|
:block => false,
|
169
172
|
:on_cancellation => nil
|
170
173
|
}, &block)
|
171
174
|
|
175
|
+
unless opts[:ack].nil?
|
176
|
+
warn "[DEPRECATION] `:ack` is deprecated. Please use `:manual_ack` instead."
|
177
|
+
opts[:manual_ack] = opts[:ack]
|
178
|
+
end
|
179
|
+
|
172
180
|
ctag = opts.fetch(:consumer_tag, @channel.generate_consumer_tag)
|
173
181
|
consumer = Consumer.new(@channel,
|
174
182
|
self,
|
175
183
|
ctag,
|
176
|
-
!
|
184
|
+
!opts[:manual_ack],
|
177
185
|
opts[:exclusive],
|
178
186
|
opts[:arguments])
|
179
187
|
|
@@ -208,7 +216,8 @@ module Bunny
|
|
208
216
|
|
209
217
|
# @param [Hash] opts Options
|
210
218
|
#
|
211
|
-
# @option opts [Boolean] :ack (false)
|
219
|
+
# @option opts [Boolean] :ack (false) [DEPRECATED] Use :manual_ack instead
|
220
|
+
# @option opts [Boolean] :manual_ack (false) Will the message be acknowledged manually?
|
212
221
|
#
|
213
222
|
# @return [Array] Triple of delivery info, message properties and message content.
|
214
223
|
# If the queue is empty, all three will be nils.
|
@@ -229,12 +238,17 @@ module Bunny
|
|
229
238
|
#
|
230
239
|
# puts "This is the message: " + payload + "\n\n"
|
231
240
|
# conn.close
|
232
|
-
def pop(opts = {:
|
241
|
+
def pop(opts = {:manual_ack => false}, &block)
|
242
|
+
unless opts[:ack].nil?
|
243
|
+
warn "[DEPRECATION] `:ack` is deprecated. Please use `:manual_ack` instead."
|
244
|
+
opts[:manual_ack] = opts[:ack]
|
245
|
+
end
|
246
|
+
|
233
247
|
get_response, properties, content = @channel.basic_get(@name, opts)
|
234
248
|
|
235
249
|
if block
|
236
250
|
if properties
|
237
|
-
di = GetResponse.new(get_response,
|
251
|
+
di = GetResponse.new(get_response, @channel)
|
238
252
|
mp = MessageProperties.new(properties)
|
239
253
|
|
240
254
|
block.call(di, mp, content)
|
@@ -243,7 +257,7 @@ module Bunny
|
|
243
257
|
end
|
244
258
|
else
|
245
259
|
if properties
|
246
|
-
di = GetResponse.new(get_response,
|
260
|
+
di = GetResponse.new(get_response, @channel)
|
247
261
|
mp = MessageProperties.new(properties)
|
248
262
|
[di, mp, content]
|
249
263
|
else
|
@@ -326,7 +340,7 @@ module Bunny
|
|
326
340
|
# TODO: inject and use logger
|
327
341
|
# puts "Recovering queue #{@name}"
|
328
342
|
begin
|
329
|
-
declare!
|
343
|
+
declare! unless @options[:no_declare]
|
330
344
|
|
331
345
|
@channel.register_queue(self)
|
332
346
|
rescue Exception => e
|
@@ -358,11 +372,6 @@ module Bunny
|
|
358
372
|
|
359
373
|
protected
|
360
374
|
|
361
|
-
# @private
|
362
|
-
def self.add_default_options(name, opts, block)
|
363
|
-
{ :queue => name, :nowait => (block.nil? && !name.empty?) }.merge(opts)
|
364
|
-
end
|
365
|
-
|
366
375
|
# @private
|
367
376
|
def self.add_default_options(name, opts)
|
368
377
|
# :nowait is always false for Bunny
|
data/lib/bunny/reader_loop.rb
CHANGED
@@ -9,13 +9,17 @@ module Bunny
|
|
9
9
|
# @private
|
10
10
|
class ReaderLoop
|
11
11
|
|
12
|
-
def initialize(transport, session,
|
13
|
-
@transport
|
14
|
-
@session
|
15
|
-
@
|
16
|
-
@logger
|
12
|
+
def initialize(transport, session, session_error_handler)
|
13
|
+
@transport = transport
|
14
|
+
@session = session
|
15
|
+
@session_error_handler = session_error_handler
|
16
|
+
@logger = @session.logger
|
17
17
|
|
18
|
-
@mutex
|
18
|
+
@mutex = Mutex.new
|
19
|
+
|
20
|
+
@stopping = false
|
21
|
+
@stopped = false
|
22
|
+
@network_is_down = false
|
19
23
|
end
|
20
24
|
|
21
25
|
|
@@ -33,15 +37,17 @@ module Bunny
|
|
33
37
|
begin
|
34
38
|
break if @mutex.synchronize { @stopping || @stopped || @network_is_down }
|
35
39
|
run_once
|
36
|
-
rescue AMQ::Protocol::EmptyResponseError, IOError, SystemCallError
|
40
|
+
rescue AMQ::Protocol::EmptyResponseError, IOError, SystemCallError, Timeout::Error,
|
41
|
+
OpenSSL::OpenSSLError => e
|
37
42
|
break if terminate? || @session.closing? || @session.closed?
|
38
43
|
|
39
|
-
log_exception(e)
|
40
44
|
@network_is_down = true
|
41
45
|
if @session.automatically_recover?
|
46
|
+
log_exception(e, level: :warn)
|
42
47
|
@session.handle_network_failure(e)
|
43
48
|
else
|
44
|
-
|
49
|
+
log_exception(e)
|
50
|
+
@session_error_handler.raise(Bunny::NetworkFailure.new("detected a network failure: #{e.message}", e))
|
45
51
|
end
|
46
52
|
rescue ShutdownSignal => _
|
47
53
|
@mutex.synchronize { @stopping = true }
|
@@ -52,9 +58,9 @@ module Bunny
|
|
52
58
|
log_exception(e)
|
53
59
|
|
54
60
|
@network_is_down = true
|
55
|
-
@
|
61
|
+
@session_error_handler.raise(Bunny::NetworkFailure.new("caught an unexpected exception in the network loop: #{e.message}", e))
|
56
62
|
end
|
57
|
-
rescue Errno::EBADF =>
|
63
|
+
rescue Errno::EBADF => _ebadf
|
58
64
|
break if terminate?
|
59
65
|
# ignored, happens when we loop after the transport has already been closed
|
60
66
|
@mutex.synchronize { @stopping = true }
|
@@ -92,11 +98,11 @@ module Bunny
|
|
92
98
|
end
|
93
99
|
|
94
100
|
def stopped?
|
95
|
-
@mutex.synchronize { @stopped
|
101
|
+
@mutex.synchronize { @stopped }
|
96
102
|
end
|
97
103
|
|
98
104
|
def stopping?
|
99
|
-
@mutex.synchronize { @stopping
|
105
|
+
@mutex.synchronize { @stopping }
|
100
106
|
end
|
101
107
|
|
102
108
|
def terminate_with(e)
|
@@ -110,7 +116,14 @@ module Bunny
|
|
110
116
|
end
|
111
117
|
|
112
118
|
def join
|
113
|
-
|
119
|
+
# Thread#join can/would trigger a re-raise of an unhandled exception in this thread.
|
120
|
+
# In addition, Thread.handle_interrupt can be used by other libraries or application code
|
121
|
+
# that would make this join operation fail with an obscure exception.
|
122
|
+
# So we try to save everyone some really unpleasant debugging time by introducing
|
123
|
+
# this condition which typically would not evaluate to true anyway.
|
124
|
+
#
|
125
|
+
# See ruby-amqp/bunny#589 and ruby-amqp/bunny#590 for background.
|
126
|
+
@thread.join if @thread && @thread != Thread.current
|
114
127
|
end
|
115
128
|
|
116
129
|
def kill
|
@@ -122,12 +135,12 @@ module Bunny
|
|
122
135
|
|
123
136
|
protected
|
124
137
|
|
125
|
-
def log_exception(e)
|
138
|
+
def log_exception(e, level: :error)
|
126
139
|
if !(io_error?(e) && (@session.closing? || @session.closed?))
|
127
|
-
@logger.
|
128
|
-
@logger.
|
140
|
+
@logger.send level, "Exception in the reader loop: #{e.class.name}: #{e.message}"
|
141
|
+
@logger.send level, "Backtrace: "
|
129
142
|
e.backtrace.each do |line|
|
130
|
-
@logger.
|
143
|
+
@logger.send level, "\t#{line}"
|
131
144
|
end
|
132
145
|
end
|
133
146
|
end
|