bunny 2.7.4 → 2.22.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +5 -5
- data/README.md +61 -35
- data/lib/bunny/channel.rb +186 -50
- data/lib/bunny/channel_id_allocator.rb +3 -1
- data/lib/bunny/consumer.rb +2 -2
- data/lib/bunny/consumer_work_pool.rb +2 -1
- data/lib/bunny/cruby/socket.rb +3 -0
- data/lib/bunny/cruby/ssl_socket.rb +6 -1
- data/lib/bunny/delivery_info.rb +1 -1
- data/lib/bunny/heartbeat_sender.rb +2 -1
- data/lib/bunny/jruby/ssl_socket.rb +5 -0
- data/lib/bunny/queue.rb +36 -8
- data/lib/bunny/reader_loop.rb +22 -10
- data/lib/bunny/session.rb +152 -65
- data/lib/bunny/test_kit.rb +14 -0
- data/lib/bunny/transport.rb +132 -49
- data/lib/bunny/version.rb +1 -1
- data/lib/bunny.rb +45 -4
- metadata +37 -225
- data/.github/ISSUE_TEMPLATE.md +0 -18
- data/.gitignore +0 -28
- data/.rspec +0 -1
- data/.travis.yml +0 -20
- data/.yardopts +0 -8
- data/CONTRIBUTING.md +0 -111
- data/ChangeLog.md +0 -1831
- data/Gemfile +0 -53
- data/LICENSE +0 -21
- data/Rakefile +0 -46
- data/benchmarks/basic_publish/with_128K_messages.rb +0 -35
- data/benchmarks/basic_publish/with_1k_messages.rb +0 -35
- data/benchmarks/basic_publish/with_4K_messages.rb +0 -35
- data/benchmarks/basic_publish/with_64K_messages.rb +0 -35
- data/benchmarks/channel_open.rb +0 -28
- data/benchmarks/mutex_and_monitor.rb +0 -42
- data/benchmarks/queue_declare.rb +0 -29
- data/benchmarks/queue_declare_and_bind.rb +0 -29
- data/benchmarks/queue_declare_bind_and_delete.rb +0 -29
- data/benchmarks/synchronized_sorted_set.rb +0 -53
- data/benchmarks/write_vs_write_nonblock.rb +0 -49
- data/bin/ci/before_build +0 -46
- data/bunny.gemspec +0 -35
- data/docker/Dockerfile +0 -16
- data/docker/docker-entrypoint.sh +0 -37
- data/docker-compose.yml +0 -18
- data/examples/connection/authentication_failure.rb +0 -16
- data/examples/connection/automatic_recovery_with_basic_get.rb +0 -40
- data/examples/connection/automatic_recovery_with_client_named_queues.rb +0 -36
- data/examples/connection/automatic_recovery_with_multiple_consumers.rb +0 -46
- data/examples/connection/automatic_recovery_with_republishing.rb +0 -109
- data/examples/connection/automatic_recovery_with_server_named_queues.rb +0 -35
- data/examples/connection/channel_level_exception.rb +0 -27
- data/examples/connection/disabled_automatic_recovery.rb +0 -34
- data/examples/connection/heartbeat.rb +0 -17
- data/examples/connection/manually_reconnecting_consumer.rb +0 -23
- data/examples/connection/manually_reconnecting_publisher.rb +0 -28
- data/examples/connection/unknown_host.rb +0 -16
- data/examples/consumers/high_and_low_priority.rb +0 -50
- data/examples/guides/exchanges/direct_exchange_routing.rb +0 -36
- data/examples/guides/exchanges/fanout_exchange_routing.rb +0 -28
- data/examples/guides/exchanges/headers_exchange_routing.rb +0 -31
- data/examples/guides/exchanges/mandatory_messages.rb +0 -30
- data/examples/guides/extensions/alternate_exchange.rb +0 -30
- data/examples/guides/extensions/basic_nack.rb +0 -33
- data/examples/guides/extensions/connection_blocked.rb +0 -35
- data/examples/guides/extensions/consumer_cancellation_notification.rb +0 -39
- data/examples/guides/extensions/dead_letter_exchange.rb +0 -32
- data/examples/guides/extensions/exchange_to_exchange_bindings.rb +0 -29
- data/examples/guides/extensions/per_message_ttl.rb +0 -36
- data/examples/guides/extensions/per_queue_message_ttl.rb +0 -36
- data/examples/guides/extensions/publisher_confirms.rb +0 -28
- data/examples/guides/extensions/queue_lease.rb +0 -26
- data/examples/guides/extensions/sender_selected_distribution.rb +0 -32
- data/examples/guides/getting_started/blabbr.rb +0 -27
- data/examples/guides/getting_started/hello_world.rb +0 -22
- data/examples/guides/getting_started/weathr.rb +0 -49
- data/examples/guides/queues/one_off_consumer.rb +0 -25
- data/examples/guides/queues/redeliveries.rb +0 -81
- data/profiling/basic_publish/with_4K_messages.rb +0 -33
- data/repl +0 -3
- data/spec/config/enabled_plugins +0 -1
- data/spec/config/rabbitmq.config +0 -19
- data/spec/higher_level_api/integration/basic_ack_spec.rb +0 -230
- data/spec/higher_level_api/integration/basic_cancel_spec.rb +0 -142
- data/spec/higher_level_api/integration/basic_consume_spec.rb +0 -349
- data/spec/higher_level_api/integration/basic_consume_with_objects_spec.rb +0 -54
- data/spec/higher_level_api/integration/basic_get_spec.rb +0 -80
- data/spec/higher_level_api/integration/basic_nack_spec.rb +0 -82
- data/spec/higher_level_api/integration/basic_publish_spec.rb +0 -74
- data/spec/higher_level_api/integration/basic_qos_spec.rb +0 -57
- data/spec/higher_level_api/integration/basic_reject_spec.rb +0 -152
- data/spec/higher_level_api/integration/basic_return_spec.rb +0 -33
- data/spec/higher_level_api/integration/channel_close_spec.rb +0 -25
- data/spec/higher_level_api/integration/channel_open_spec.rb +0 -57
- data/spec/higher_level_api/integration/connection_recovery_spec.rb +0 -471
- data/spec/higher_level_api/integration/connection_spec.rb +0 -559
- data/spec/higher_level_api/integration/connection_stop_spec.rb +0 -83
- data/spec/higher_level_api/integration/consumer_cancellation_notification_spec.rb +0 -128
- data/spec/higher_level_api/integration/dead_lettering_spec.rb +0 -75
- data/spec/higher_level_api/integration/exchange_bind_spec.rb +0 -31
- data/spec/higher_level_api/integration/exchange_declare_spec.rb +0 -237
- data/spec/higher_level_api/integration/exchange_delete_spec.rb +0 -105
- data/spec/higher_level_api/integration/exchange_unbind_spec.rb +0 -40
- data/spec/higher_level_api/integration/exclusive_queue_spec.rb +0 -28
- data/spec/higher_level_api/integration/heartbeat_spec.rb +0 -49
- data/spec/higher_level_api/integration/merry_go_round_spec.rb +0 -85
- data/spec/higher_level_api/integration/message_properties_access_spec.rb +0 -95
- data/spec/higher_level_api/integration/predeclared_exchanges_spec.rb +0 -24
- data/spec/higher_level_api/integration/publisher_confirms_spec.rb +0 -191
- data/spec/higher_level_api/integration/publishing_edge_cases_spec.rb +0 -87
- data/spec/higher_level_api/integration/queue_bind_spec.rb +0 -109
- data/spec/higher_level_api/integration/queue_declare_spec.rb +0 -221
- data/spec/higher_level_api/integration/queue_delete_spec.rb +0 -41
- data/spec/higher_level_api/integration/queue_purge_spec.rb +0 -30
- data/spec/higher_level_api/integration/queue_unbind_spec.rb +0 -54
- data/spec/higher_level_api/integration/read_only_consumer_spec.rb +0 -60
- data/spec/higher_level_api/integration/sender_selected_distribution_spec.rb +0 -36
- data/spec/higher_level_api/integration/tls_connection_spec.rb +0 -222
- data/spec/higher_level_api/integration/tx_commit_spec.rb +0 -21
- data/spec/higher_level_api/integration/tx_rollback_spec.rb +0 -21
- data/spec/higher_level_api/integration/with_channel_spec.rb +0 -25
- data/spec/issues/issue100_spec.rb +0 -42
- data/spec/issues/issue141_spec.rb +0 -43
- data/spec/issues/issue202_spec.rb +0 -15
- data/spec/issues/issue224_spec.rb +0 -40
- data/spec/issues/issue465_spec.rb +0 -32
- data/spec/issues/issue78_spec.rb +0 -72
- data/spec/issues/issue83_spec.rb +0 -30
- data/spec/issues/issue97_attachment.json +0 -1
- data/spec/issues/issue97_spec.rb +0 -175
- data/spec/lower_level_api/integration/basic_cancel_spec.rb +0 -83
- data/spec/lower_level_api/integration/basic_consume_spec.rb +0 -99
- data/spec/spec_helper.rb +0 -51
- data/spec/stress/channel_close_stress_spec.rb +0 -64
- data/spec/stress/channel_open_stress_spec.rb +0 -84
- data/spec/stress/channel_open_stress_with_single_threaded_connection_spec.rb +0 -28
- data/spec/stress/concurrent_consumers_stress_spec.rb +0 -71
- data/spec/stress/concurrent_publishers_stress_spec.rb +0 -54
- data/spec/stress/connection_open_close_spec.rb +0 -52
- data/spec/stress/long_running_consumer_spec.rb +0 -84
- data/spec/tls/ca_certificate.pem +0 -29
- data/spec/tls/ca_key.pem +0 -52
- data/spec/tls/client_certificate.pem +0 -29
- data/spec/tls/client_key.pem +0 -51
- data/spec/tls/generate-server-cert.sh +0 -8
- data/spec/tls/server-openssl.cnf +0 -10
- data/spec/tls/server.csr +0 -16
- data/spec/tls/server_certificate.pem +0 -29
- data/spec/tls/server_key.pem +0 -51
- data/spec/unit/bunny_spec.rb +0 -15
- data/spec/unit/concurrent/atomic_fixnum_spec.rb +0 -35
- data/spec/unit/concurrent/condition_spec.rb +0 -82
- data/spec/unit/concurrent/linked_continuation_queue_spec.rb +0 -35
- data/spec/unit/concurrent/synchronized_sorted_set_spec.rb +0 -73
- data/spec/unit/exchange_recovery_spec.rb +0 -39
- data/spec/unit/version_delivery_tag_spec.rb +0 -28
data/lib/bunny/queue.rb
CHANGED
@@ -11,6 +11,23 @@ module Bunny
|
|
11
11
|
# API
|
12
12
|
#
|
13
13
|
|
14
|
+
module Types
|
15
|
+
QUORUM = "quorum"
|
16
|
+
CLASSIC = "classic"
|
17
|
+
STREAM = "stream"
|
18
|
+
|
19
|
+
KNOWN = [CLASSIC, QUORUM, STREAM]
|
20
|
+
|
21
|
+
def self.known?(q_type)
|
22
|
+
KNOWN.include?(q_type)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
module XArgs
|
27
|
+
MAX_LENGTH = "x-max-length",
|
28
|
+
QUEUE_TYPE = "x-queue-type"
|
29
|
+
end
|
30
|
+
|
14
31
|
# @return [Bunny::Channel] Channel this queue uses
|
15
32
|
attr_reader :channel
|
16
33
|
# @return [String] Queue name
|
@@ -25,7 +42,8 @@ module Bunny
|
|
25
42
|
# @option opts [Boolean] :durable (false) Should this queue be durable?
|
26
43
|
# @option opts [Boolean] :auto_delete (false) Should this queue be automatically deleted when the last consumer disconnects?
|
27
44
|
# @option opts [Boolean] :exclusive (false) Should this queue be exclusive (only can be used by this connection, removed when the connection is closed)?
|
28
|
-
# @option opts [
|
45
|
+
# @option opts [String] :type (nil) Type of the declared queue (classic, quorum or stream)
|
46
|
+
# @option opts [Hash] :arguments (nil) Additional optional arguments (typically used by RabbitMQ extensions and plugins)
|
29
47
|
#
|
30
48
|
# @see Bunny::Channel#queue
|
31
49
|
# @see http://rubybunny.info/articles/queues.html Queues and Consumers guide
|
@@ -42,7 +60,17 @@ module Bunny
|
|
42
60
|
@exclusive = @options[:exclusive]
|
43
61
|
@server_named = @name.empty?
|
44
62
|
@auto_delete = @options[:auto_delete]
|
45
|
-
@
|
63
|
+
@type = @options[:type]
|
64
|
+
|
65
|
+
@arguments = if @type and !@type.empty? then
|
66
|
+
(@options[:arguments] || {}).merge({XArgs::QUEUE_TYPE => @type})
|
67
|
+
else
|
68
|
+
@options[:arguments]
|
69
|
+
end
|
70
|
+
verify_type!(@arguments)
|
71
|
+
# reassigns updated and verified arguments because Bunny::Channel#declare_queue
|
72
|
+
# accepts a map of options
|
73
|
+
@options[:arguments] = @arguments
|
46
74
|
|
47
75
|
@bindings = Array.new
|
48
76
|
|
@@ -158,7 +186,6 @@ module Bunny
|
|
158
186
|
# @option opts [Boolean] :ack (false) [DEPRECATED] Use :manual_ack instead
|
159
187
|
# @option opts [Boolean] :manual_ack (false) Will this consumer use manual acknowledgements?
|
160
188
|
# @option opts [Boolean] :exclusive (false) Should this consumer be exclusive for this queue?
|
161
|
-
# @option opts [Boolean] :block (false) Should the call block calling thread?
|
162
189
|
# @option opts [#call] :on_cancellation Block to execute when this consumer is cancelled remotely (e.g. via the RabbitMQ Management plugin)
|
163
190
|
# @option opts [String] :consumer_tag Unique consumer identifier. It is usually recommended to let Bunny generate it for you.
|
164
191
|
# @option opts [Hash] :arguments ({}) Additional (optional) arguments, typically used by RabbitMQ extensions
|
@@ -373,11 +400,6 @@ module Bunny
|
|
373
400
|
|
374
401
|
protected
|
375
402
|
|
376
|
-
# @private
|
377
|
-
def self.add_default_options(name, opts, block)
|
378
|
-
{ :queue => name, :nowait => (block.nil? && !name.empty?) }.merge(opts)
|
379
|
-
end
|
380
|
-
|
381
403
|
# @private
|
382
404
|
def self.add_default_options(name, opts)
|
383
405
|
# :nowait is always false for Bunny
|
@@ -395,5 +417,11 @@ module Bunny
|
|
395
417
|
h
|
396
418
|
end
|
397
419
|
end
|
420
|
+
|
421
|
+
def verify_type!(args)
|
422
|
+
q_type = (args || {})["x-queue-type"]
|
423
|
+
throw ArgumentError.new(
|
424
|
+
"unsupported queue type #{q_type.inspect}, supported ones: #{Types::KNOWN.join(', ')}") if (q_type and !Types.known?(q_type))
|
425
|
+
end
|
398
426
|
end
|
399
427
|
end
|
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,7 +37,8 @@ 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, Timeout::Error
|
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
44
|
@network_is_down = true
|
@@ -42,7 +47,7 @@ module Bunny
|
|
42
47
|
@session.handle_network_failure(e)
|
43
48
|
else
|
44
49
|
log_exception(e)
|
45
|
-
@
|
50
|
+
@session_error_handler.raise(Bunny::NetworkFailure.new("detected a network failure: #{e.message}", e))
|
46
51
|
end
|
47
52
|
rescue ShutdownSignal => _
|
48
53
|
@mutex.synchronize { @stopping = true }
|
@@ -53,7 +58,7 @@ module Bunny
|
|
53
58
|
log_exception(e)
|
54
59
|
|
55
60
|
@network_is_down = true
|
56
|
-
@
|
61
|
+
@session_error_handler.raise(Bunny::NetworkFailure.new("caught an unexpected exception in the network loop: #{e.message}", e))
|
57
62
|
end
|
58
63
|
rescue Errno::EBADF => _ebadf
|
59
64
|
break if terminate?
|
@@ -111,7 +116,14 @@ module Bunny
|
|
111
116
|
end
|
112
117
|
|
113
118
|
def join
|
114
|
-
|
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
|
115
127
|
end
|
116
128
|
|
117
129
|
def kill
|
data/lib/bunny/session.rb
CHANGED
@@ -36,10 +36,10 @@ module Bunny
|
|
36
36
|
DEFAULT_HEARTBEAT = :server
|
37
37
|
# @private
|
38
38
|
DEFAULT_FRAME_MAX = 131072
|
39
|
-
#
|
39
|
+
# Hard limit the user cannot go over regardless of server configuration.
|
40
40
|
# @private
|
41
41
|
CHANNEL_MAX_LIMIT = 65535
|
42
|
-
DEFAULT_CHANNEL_MAX =
|
42
|
+
DEFAULT_CHANNEL_MAX = 2047
|
43
43
|
|
44
44
|
# backwards compatibility
|
45
45
|
# @private
|
@@ -71,6 +71,7 @@ module Bunny
|
|
71
71
|
# Default reconnection interval for TCP connection failures
|
72
72
|
DEFAULT_NETWORK_RECOVERY_INTERVAL = 5.0
|
73
73
|
|
74
|
+
DEFAULT_RECOVERABLE_EXCEPTIONS = [StandardError, TCPConnectionFailedForAllHosts, TCPConnectionFailed, AMQ::Protocol::EmptyResponseError, SystemCallError, Timeout::Error, Bunny::ConnectionLevelException, Bunny::ConnectionClosedError]
|
74
75
|
|
75
76
|
#
|
76
77
|
# API
|
@@ -78,7 +79,7 @@ module Bunny
|
|
78
79
|
|
79
80
|
# @return [Bunny::Transport]
|
80
81
|
attr_reader :transport
|
81
|
-
attr_reader :status, :
|
82
|
+
attr_reader :status, :heartbeat, :user, :pass, :vhost, :frame_max, :channel_max, :threaded
|
82
83
|
attr_reader :server_capabilities, :server_properties, :server_authentication_mechanisms, :server_locales
|
83
84
|
attr_reader :channel_id_allocator
|
84
85
|
# Authentication mechanism, e.g. "PLAIN" or "EXTERNAL"
|
@@ -89,7 +90,9 @@ module Bunny
|
|
89
90
|
# @return [Integer] Timeout for blocking protocol operations (queue.declare, queue.bind, etc), in milliseconds. Default is 15000.
|
90
91
|
attr_reader :continuation_timeout
|
91
92
|
attr_reader :network_recovery_interval
|
93
|
+
attr_reader :connection_name
|
92
94
|
attr_accessor :socket_configurator
|
95
|
+
attr_accessor :recoverable_exceptions
|
93
96
|
|
94
97
|
# @param [String, Hash] connection_string_or_opts Connection string or a hash of connection options
|
95
98
|
# @param [Hash] optz Extra options not related to connection
|
@@ -101,19 +104,21 @@ module Bunny
|
|
101
104
|
# @option connection_string_or_opts [String] :username ("guest") Username
|
102
105
|
# @option connection_string_or_opts [String] :password ("guest") Password
|
103
106
|
# @option connection_string_or_opts [String] :vhost ("/") Virtual host to use
|
104
|
-
# @option connection_string_or_opts [Integer, Symbol] :heartbeat (:server) Heartbeat
|
107
|
+
# @option connection_string_or_opts [Integer, Symbol] :heartbeat (:server) Heartbeat timeout to offer to the server. :server means use the value suggested by RabbitMQ. 0 means heartbeats and socket read timeouts will be disabled (not recommended).
|
105
108
|
# @option connection_string_or_opts [Integer] :network_recovery_interval (4) Recovery interval periodic network recovery will use. This includes initial pause after network failure.
|
106
109
|
# @option connection_string_or_opts [Boolean] :tls (false) Should TLS/SSL be used?
|
107
110
|
# @option connection_string_or_opts [String] :tls_cert (nil) Path to client TLS/SSL certificate file (.pem)
|
108
111
|
# @option connection_string_or_opts [String] :tls_key (nil) Path to client TLS/SSL private key file (.pem)
|
109
112
|
# @option connection_string_or_opts [Array<String>] :tls_ca_certificates Array of paths to TLS/SSL CA files (.pem), by default detected from OpenSSL configuration
|
110
113
|
# @option connection_string_or_opts [String] :verify_peer (true) Whether TLS peer verification should be performed
|
111
|
-
# @option connection_string_or_opts [Symbol] :
|
114
|
+
# @option connection_string_or_opts [Symbol] :tls_protocol (negotiated) What TLS version should be used (:TLSv1, :TLSv1_1, or :TLSv1_2)
|
115
|
+
# @option connection_string_or_opts [Integer] :channel_max (2047) Maximum number of channels allowed on this connection, minus 1 to account for the special channel 0.
|
112
116
|
# @option connection_string_or_opts [Integer] :continuation_timeout (15000) Timeout for client operations that expect a response (e.g. {Bunny::Queue#get}), in milliseconds.
|
113
117
|
# @option connection_string_or_opts [Integer] :connection_timeout (30) Timeout in seconds for connecting to the server.
|
114
|
-
# @option connection_string_or_opts [Integer] :read_timeout (30) TCP socket read timeout in seconds.
|
118
|
+
# @option connection_string_or_opts [Integer] :read_timeout (30) TCP socket read timeout in seconds. If heartbeats are disabled this will be ignored.
|
115
119
|
# @option connection_string_or_opts [Integer] :write_timeout (30) TCP socket write timeout in seconds.
|
116
|
-
# @option connection_string_or_opts [Proc] :hosts_shuffle_strategy
|
120
|
+
# @option connection_string_or_opts [Proc] :hosts_shuffle_strategy a callable that reorders a list of host strings, defaults to Array#shuffle
|
121
|
+
# @option connection_string_or_opts [Proc] :recovery_completed a callable that will be called when a network recovery is performed
|
117
122
|
# @option connection_string_or_opts [Logger] :logger The logger. If missing, one is created using :log_file and :log_level.
|
118
123
|
# @option connection_string_or_opts [IO, String] :log_file The file or path to use when creating a logger. Defaults to STDOUT.
|
119
124
|
# @option connection_string_or_opts [IO, String] :logfile DEPRECATED: use :log_file instead. The file or path to use when creating a logger. Defaults to STDOUT.
|
@@ -121,10 +126,15 @@ module Bunny
|
|
121
126
|
# @option connection_string_or_opts [Boolean] :automatically_recover (true) Should automatically recover from network failures?
|
122
127
|
# @option connection_string_or_opts [Integer] :recovery_attempts (nil) Max number of recovery attempts, nil means forever
|
123
128
|
# @option connection_string_or_opts [Integer] :reset_recovery_attempts_after_reconnection (true) Should recovery attempt counter be reset after successful reconnection? When set to false, the attempt counter will last through the entire lifetime of the connection object.
|
129
|
+
# @option connection_string_or_opts [Proc] :recovery_attempt_started (nil) Will be called before every connection recovery attempt
|
130
|
+
# @option connection_string_or_opts [Proc] :recovery_completed (nil) Will be called after successful connection recovery
|
131
|
+
# @option connection_string_or_opts [Proc] :recovery_attempts_exhausted (nil) Will be called when the connection recovery failed after the specified amount of recovery attempts
|
124
132
|
# @option connection_string_or_opts [Boolean] :recover_from_connection_close (true) Should this connection recover after receiving a server-sent connection.close (e.g. connection was force closed)?
|
133
|
+
# @option connection_string_or_opts [Object] :session_error_handler (Thread.current) Object which responds to #raise that will act as a session error handler. Defaults to Thread.current, which will raise asynchronous exceptions in the thread that created the session.
|
125
134
|
#
|
126
135
|
# @option optz [String] :auth_mechanism ("PLAIN") Authentication mechanism, PLAIN or EXTERNAL
|
127
136
|
# @option optz [String] :locale ("PLAIN") Locale RabbitMQ should use
|
137
|
+
# @option optz [String] :connection_name (nil) Client-provided connection name, if any. Note that the value returned does not uniquely identify a connection and cannot be used as a connection identifier in HTTP API requests.
|
128
138
|
#
|
129
139
|
# @see http://rubybunny.info/articles/connecting.html Connecting to RabbitMQ guide
|
130
140
|
# @see http://rubybunny.info/articles/tls.html TLS/SSL guide
|
@@ -151,22 +161,25 @@ module Bunny
|
|
151
161
|
@addresses = self.addresses_from(opts)
|
152
162
|
@address_index = 0
|
153
163
|
|
154
|
-
|
155
|
-
@logger = opts.fetch(:logger, init_default_logger(log_file, log_level))
|
156
|
-
|
164
|
+
@transport = nil
|
157
165
|
@user = self.username_from(opts)
|
158
166
|
@pass = self.password_from(opts)
|
159
167
|
@vhost = self.vhost_from(opts)
|
160
168
|
@threaded = opts.fetch(:threaded, true)
|
161
169
|
|
170
|
+
# re-init, see above
|
171
|
+
@logger = opts.fetch(:logger, init_default_logger(log_file, log_level))
|
172
|
+
|
162
173
|
validate_connection_options(opts)
|
174
|
+
@last_connection_error = nil
|
163
175
|
|
164
176
|
# should automatic recovery from network failures be used?
|
165
177
|
@automatically_recover = if opts[:automatically_recover].nil? && opts[:automatic_recovery].nil?
|
166
178
|
true
|
167
179
|
else
|
168
|
-
opts[:automatically_recover]
|
180
|
+
opts[:automatically_recover] | opts[:automatic_recovery]
|
169
181
|
end
|
182
|
+
@recovering_from_network_failure = false
|
170
183
|
@max_recovery_attempts = opts[:recovery_attempts]
|
171
184
|
@recovery_attempts = @max_recovery_attempts
|
172
185
|
# When this is set, connection attempts won't be reset after
|
@@ -180,6 +193,7 @@ module Bunny
|
|
180
193
|
@continuation_timeout = opts.fetch(:continuation_timeout, DEFAULT_CONTINUATION_TIMEOUT)
|
181
194
|
|
182
195
|
@status = :not_connected
|
196
|
+
@manually_closed = false
|
183
197
|
@blocked = false
|
184
198
|
|
185
199
|
# these are negotiated with the broker during the connection tuning phase
|
@@ -187,10 +201,13 @@ module Bunny
|
|
187
201
|
@client_channel_max = normalize_client_channel_max(opts.fetch(:channel_max, DEFAULT_CHANNEL_MAX))
|
188
202
|
# will be-renegotiated during connection tuning steps. MK.
|
189
203
|
@channel_max = @client_channel_max
|
204
|
+
@heartbeat_sender = nil
|
190
205
|
@client_heartbeat = self.heartbeat_from(opts)
|
191
206
|
|
192
207
|
client_props = opts[:properties] || opts[:client_properties] || {}
|
208
|
+
@connection_name = client_props[:connection_name] || opts[:connection_name]
|
193
209
|
@client_properties = DEFAULT_CLIENT_PROPERTIES.merge(client_props)
|
210
|
+
.merge(connection_name: connection_name)
|
194
211
|
@mechanism = normalize_auth_mechanism(opts.fetch(:auth_mechanism, "PLAIN"))
|
195
212
|
@credentials_encoder = credentials_encoder_for(@mechanism)
|
196
213
|
@locale = @opts.fetch(:locale, DEFAULT_LOCALE)
|
@@ -207,10 +224,17 @@ module Bunny
|
|
207
224
|
|
208
225
|
@channels = Hash.new
|
209
226
|
|
210
|
-
@
|
227
|
+
@recovery_attempt_started = opts[:recovery_attempt_started]
|
228
|
+
@recovery_completed = opts[:recovery_completed]
|
229
|
+
@recovery_attempts_exhausted = opts[:recovery_attempts_exhausted]
|
230
|
+
|
231
|
+
@session_error_handler = opts.fetch(:session_error_handler, Thread.current)
|
232
|
+
|
233
|
+
@recoverable_exceptions = DEFAULT_RECOVERABLE_EXCEPTIONS.dup
|
211
234
|
|
212
235
|
self.reset_continuations
|
213
236
|
self.initialize_transport
|
237
|
+
|
214
238
|
end
|
215
239
|
|
216
240
|
def validate_connection_options(options)
|
@@ -232,9 +256,13 @@ module Bunny
|
|
232
256
|
# @return [String] Virtual host used
|
233
257
|
def virtual_host; self.vhost; end
|
234
258
|
|
235
|
-
# @
|
259
|
+
# @deprecated
|
260
|
+
# @return [Integer] Heartbeat timeout (not interval) used
|
236
261
|
def heartbeat_interval; self.heartbeat; end
|
237
262
|
|
263
|
+
# @return [Integer] Heartbeat timeout used
|
264
|
+
def heartbeat_timeout; self.heartbeat; end
|
265
|
+
|
238
266
|
# @return [Boolean] true if this connection uses TLS (SSL)
|
239
267
|
def uses_tls?
|
240
268
|
@transport.uses_tls?
|
@@ -303,10 +331,6 @@ module Bunny
|
|
303
331
|
@transport.post_initialize_socket
|
304
332
|
@transport.connect
|
305
333
|
|
306
|
-
if @socket_configurator
|
307
|
-
@transport.configure_socket(&@socket_configurator)
|
308
|
-
end
|
309
|
-
|
310
334
|
self.init_connection
|
311
335
|
self.open_connection
|
312
336
|
|
@@ -333,6 +357,14 @@ module Bunny
|
|
333
357
|
self
|
334
358
|
end
|
335
359
|
|
360
|
+
def update_secret(value, reason)
|
361
|
+
@transport.send_frame(AMQ::Protocol::Connection::UpdateSecret.encode(value, reason))
|
362
|
+
@last_update_secret_ok = wait_on_continuations
|
363
|
+
raise_if_continuation_resulted_in_a_connection_error!
|
364
|
+
|
365
|
+
@last_update_secret_ok
|
366
|
+
end
|
367
|
+
|
336
368
|
# Socket operation write timeout used by this connection
|
337
369
|
# @return [Integer]
|
338
370
|
# @private
|
@@ -363,14 +395,16 @@ module Bunny
|
|
363
395
|
alias channel create_channel
|
364
396
|
|
365
397
|
# Closes the connection. This involves closing all of its channels.
|
366
|
-
def close
|
398
|
+
def close(await_response = true)
|
367
399
|
@status_mutex.synchronize { @status = :closing }
|
368
400
|
|
369
401
|
ignoring_io_errors do
|
370
402
|
if @transport.open?
|
403
|
+
@logger.debug "Transport is still open..."
|
371
404
|
close_all_channels
|
372
405
|
|
373
|
-
|
406
|
+
@logger.debug "Will close all channels...."
|
407
|
+
self.close_connection(await_response)
|
374
408
|
end
|
375
409
|
|
376
410
|
clean_up_on_shutdown
|
@@ -379,6 +413,8 @@ module Bunny
|
|
379
413
|
@status = :closed
|
380
414
|
@manually_closed = true
|
381
415
|
end
|
416
|
+
@logger.debug "Connection is closed"
|
417
|
+
true
|
382
418
|
end
|
383
419
|
alias stop close
|
384
420
|
|
@@ -413,7 +449,7 @@ module Bunny
|
|
413
449
|
@status_mutex.synchronize { @status == :closed }
|
414
450
|
end
|
415
451
|
|
416
|
-
# @return [Boolean] true if this AMQP 0.9.1 connection has been
|
452
|
+
# @return [Boolean] true if this AMQP 0.9.1 connection has been closed by the user (as opposed to the server)
|
417
453
|
def manually_closed?
|
418
454
|
@status_mutex.synchronize { @manually_closed == true }
|
419
455
|
end
|
@@ -464,7 +500,7 @@ module Bunny
|
|
464
500
|
# @param [String] uri amqp or amqps URI to parse
|
465
501
|
# @return [Hash] Parsed URI as a hash
|
466
502
|
def self.parse_uri(uri)
|
467
|
-
AMQ::Settings.
|
503
|
+
AMQ::Settings.configure(uri)
|
468
504
|
end
|
469
505
|
|
470
506
|
# Checks if a queue with given name exists.
|
@@ -507,6 +543,24 @@ module Bunny
|
|
507
543
|
end
|
508
544
|
end
|
509
545
|
|
546
|
+
# Defines a callable (e.g. a block) that will be called
|
547
|
+
# before every connection recovery attempt.
|
548
|
+
def before_recovery_attempt_starts(&block)
|
549
|
+
@recovery_attempt_started = block
|
550
|
+
end
|
551
|
+
|
552
|
+
# Defines a callable (e.g. a block) that will be called
|
553
|
+
# after successful connection recovery.
|
554
|
+
def after_recovery_completed(&block)
|
555
|
+
@recovery_completed = block
|
556
|
+
end
|
557
|
+
|
558
|
+
# Defines a callable (e.g. a block) that will be called
|
559
|
+
# when the connection recovery failed after the specified
|
560
|
+
# numbers of recovery attempts.
|
561
|
+
def after_recovery_attempts_exhausted(&block)
|
562
|
+
@recovery_attempts_exhausted = block
|
563
|
+
end
|
510
564
|
|
511
565
|
#
|
512
566
|
# Implementation
|
@@ -563,11 +617,13 @@ module Bunny
|
|
563
617
|
end
|
564
618
|
|
565
619
|
# @private
|
566
|
-
def close_connection(
|
620
|
+
def close_connection(await_response = true)
|
567
621
|
if @transport.open?
|
622
|
+
@logger.debug "Transport is still open"
|
568
623
|
@transport.send_frame(AMQ::Protocol::Connection::Close.encode(200, "Goodbye", 0, 0))
|
569
624
|
|
570
|
-
if
|
625
|
+
if await_response
|
626
|
+
@logger.debug "Waiting for a connection.close-ok..."
|
571
627
|
@last_connection_close_ok = wait_on_continuations
|
572
628
|
end
|
573
629
|
end
|
@@ -617,6 +673,8 @@ module Bunny
|
|
617
673
|
when AMQ::Protocol::Connection::Unblocked then
|
618
674
|
@blocked = false
|
619
675
|
@unblock_callback.call(method) if @unblock_callback
|
676
|
+
when AMQ::Protocol::Connection::UpdateSecretOk then
|
677
|
+
@continuations.push(method)
|
620
678
|
when AMQ::Protocol::Channel::Close then
|
621
679
|
begin
|
622
680
|
ch = synchronised_find_channel(ch_number)
|
@@ -625,8 +683,12 @@ module Bunny
|
|
625
683
|
# avoid doing that while holding a mutex lock. MK.
|
626
684
|
ch.handle_method(method)
|
627
685
|
ensure
|
628
|
-
|
629
|
-
|
686
|
+
if ch.nil?
|
687
|
+
@logger.warn "Received a server-sent channel.close but the channel was not found locally. Ignoring the frame."
|
688
|
+
else
|
689
|
+
# synchronises on @channel_mutex under the hood
|
690
|
+
self.unregister_channel(ch)
|
691
|
+
end
|
630
692
|
end
|
631
693
|
when AMQ::Protocol::Basic::GetEmpty then
|
632
694
|
ch = find_channel(ch_number)
|
@@ -697,9 +759,7 @@ module Bunny
|
|
697
759
|
|
698
760
|
# @private
|
699
761
|
def recoverable_network_failure?(exception)
|
700
|
-
|
701
|
-
# So just recover unconditionally. MK.
|
702
|
-
true
|
762
|
+
@recoverable_exceptions.any? {|x| exception.kind_of? x}
|
703
763
|
end
|
704
764
|
|
705
765
|
# @private
|
@@ -720,6 +780,7 @@ module Bunny
|
|
720
780
|
def recover_from_network_failure
|
721
781
|
sleep @network_recovery_interval
|
722
782
|
@logger.debug "Will attempt connection recovery..."
|
783
|
+
notify_of_recovery_attempt_start
|
723
784
|
|
724
785
|
self.initialize_transport
|
725
786
|
|
@@ -738,20 +799,28 @@ module Bunny
|
|
738
799
|
end
|
739
800
|
|
740
801
|
recover_channels
|
802
|
+
notify_of_recovery_completion
|
741
803
|
end
|
742
804
|
rescue HostListDepleted
|
743
805
|
reset_address_index
|
744
806
|
retry
|
745
|
-
rescue
|
746
|
-
|
747
|
-
|
748
|
-
|
749
|
-
|
807
|
+
rescue => e
|
808
|
+
if recoverable_network_failure?(e)
|
809
|
+
@logger.warn "TCP connection failed"
|
810
|
+
if should_retry_recovery?
|
811
|
+
@logger.warn "Reconnecting in #{@network_recovery_interval} seconds"
|
812
|
+
decrement_recovery_attemp_counter!
|
750
813
|
announce_network_failure_recovery
|
751
814
|
retry
|
815
|
+
else
|
816
|
+
@logger.error "Ran out of recovery attempts (limit set to #{@max_recovery_attempts}), giving up"
|
817
|
+
@transport.close
|
818
|
+
self.close(false)
|
819
|
+
@manually_closed = false
|
820
|
+
notify_of_recovery_attempts_exhausted
|
752
821
|
end
|
753
822
|
else
|
754
|
-
|
823
|
+
raise e
|
755
824
|
end
|
756
825
|
end
|
757
826
|
|
@@ -767,8 +836,10 @@ module Bunny
|
|
767
836
|
|
768
837
|
# @private
|
769
838
|
def decrement_recovery_attemp_counter!
|
770
|
-
|
771
|
-
|
839
|
+
if @recovery_attempts
|
840
|
+
@recovery_attempts -= 1
|
841
|
+
@logger.debug "#{@recovery_attempts} recovery attempts left"
|
842
|
+
end
|
772
843
|
@recovery_attempts
|
773
844
|
end
|
774
845
|
|
@@ -782,12 +853,26 @@ module Bunny
|
|
782
853
|
@channel_mutex.synchronize do
|
783
854
|
@channels.each do |n, ch|
|
784
855
|
ch.open
|
785
|
-
|
786
856
|
ch.recover_from_network_failure
|
787
857
|
end
|
788
858
|
end
|
789
859
|
end
|
790
860
|
|
861
|
+
# @private
|
862
|
+
def notify_of_recovery_attempt_start
|
863
|
+
@recovery_attempt_started.call if @recovery_attempt_started
|
864
|
+
end
|
865
|
+
|
866
|
+
# @private
|
867
|
+
def notify_of_recovery_completion
|
868
|
+
@recovery_completed.call if @recovery_completed
|
869
|
+
end
|
870
|
+
|
871
|
+
# @private
|
872
|
+
def notify_of_recovery_attempts_exhausted
|
873
|
+
@recovery_attempts_exhausted.call if @recovery_attempts_exhausted
|
874
|
+
end
|
875
|
+
|
791
876
|
# @private
|
792
877
|
def instantiate_connection_level_exception(frame)
|
793
878
|
case frame
|
@@ -823,7 +908,7 @@ module Bunny
|
|
823
908
|
|
824
909
|
clean_up_on_shutdown
|
825
910
|
if threaded?
|
826
|
-
@
|
911
|
+
@session_error_handler.raise(@last_connection_error)
|
827
912
|
else
|
828
913
|
raise @last_connection_error
|
829
914
|
end
|
@@ -943,7 +1028,7 @@ module Bunny
|
|
943
1028
|
|
944
1029
|
# @private
|
945
1030
|
def heartbeat_from(options)
|
946
|
-
options[:heartbeat] || options[:
|
1031
|
+
options[:heartbeat] || options[:heartbeat_timeout] || options[:requested_heartbeat] || options[:heartbeat_interval] || DEFAULT_HEARTBEAT
|
947
1032
|
end
|
948
1033
|
|
949
1034
|
# @private
|
@@ -980,7 +1065,7 @@ module Bunny
|
|
980
1065
|
|
981
1066
|
# @private
|
982
1067
|
def reader_loop
|
983
|
-
@reader_loop ||= ReaderLoop.new(@transport, self,
|
1068
|
+
@reader_loop ||= ReaderLoop.new(@transport, self, @session_error_handler)
|
984
1069
|
end
|
985
1070
|
|
986
1071
|
# @private
|
@@ -1068,12 +1153,18 @@ module Bunny
|
|
1068
1153
|
# threads publish on the same channel aggressively, at some point frames will be
|
1069
1154
|
# delivered out of order and broker will raise 505 UNEXPECTED_FRAME exception.
|
1070
1155
|
# If we synchronize on the channel, however, this is both thread safe and pretty fine-grained
|
1071
|
-
# locking. Note that "single frame" methods do not need this kind of synchronization
|
1156
|
+
# locking. Note that "single frame" methods technically do not need this kind of synchronization
|
1157
|
+
# (no incorrect frame interleaving of the same kind as with basic.publish isn't possible) but we
|
1158
|
+
# still recommend not sharing channels between threads except for consumer-only cases in the docs. MK.
|
1072
1159
|
channel.synchronize do
|
1073
1160
|
# see rabbitmq/rabbitmq-server#156
|
1074
|
-
|
1075
|
-
|
1076
|
-
|
1161
|
+
if open?
|
1162
|
+
data = frames.reduce("") { |acc, frame| acc << frame.encode }
|
1163
|
+
@transport.write(data)
|
1164
|
+
signal_activity!
|
1165
|
+
else
|
1166
|
+
raise ConnectionClosedError.new(frames)
|
1167
|
+
end
|
1077
1168
|
end
|
1078
1169
|
end # send_frameset(frames)
|
1079
1170
|
|
@@ -1087,10 +1178,14 @@ module Bunny
|
|
1087
1178
|
# threads publish on the same channel aggressively, at some point frames will be
|
1088
1179
|
# delivered out of order and broker will raise 505 UNEXPECTED_FRAME exception.
|
1089
1180
|
# If we synchronize on the channel, however, this is both thread safe and pretty fine-grained
|
1090
|
-
# locking.
|
1181
|
+
# locking. See a note about "single frame" methods in a comment in `send_frameset`. MK.
|
1091
1182
|
channel.synchronize do
|
1092
|
-
|
1093
|
-
|
1183
|
+
if open?
|
1184
|
+
frames.each { |frame| self.send_frame_without_timeout(frame, false) }
|
1185
|
+
signal_activity!
|
1186
|
+
else
|
1187
|
+
raise ConnectionClosedError.new(frames)
|
1188
|
+
end
|
1094
1189
|
end
|
1095
1190
|
end # send_frameset_without_timeout(frames)
|
1096
1191
|
|
@@ -1177,26 +1272,18 @@ module Bunny
|
|
1177
1272
|
@logger.debug { "Heartbeat interval negotiation: client = #{@client_heartbeat}, server = #{connection_tune.heartbeat}, result = #{@heartbeat}" }
|
1178
1273
|
@logger.info "Heartbeat interval used (in seconds): #{@heartbeat}"
|
1179
1274
|
|
1180
|
-
# We set the read_write_timeout to twice the heartbeat value
|
1275
|
+
# We set the read_write_timeout to twice the heartbeat value,
|
1276
|
+
# and then some padding for edge cases.
|
1181
1277
|
# This allows us to miss a single heartbeat before we time out the socket.
|
1182
|
-
#
|
1183
|
-
#
|
1184
|
-
|
1185
|
-
|
1186
|
-
@transport.read_timeout = if heartbeat_disabled?(@client_heartbeat) || heartbeat_disabled?(@heartbeat)
|
1187
|
-
@logger.debug { "Will use default socket read timeout of #{Transport::DEFAULT_READ_TIMEOUT}" }
|
1188
|
-
Transport::DEFAULT_READ_TIMEOUT
|
1189
|
-
else
|
1190
|
-
# pad to account for edge cases. MK.
|
1191
|
-
n = @heartbeat * 2.2
|
1192
|
-
@logger.debug { "Will use socket read timeout of #{n}" }
|
1193
|
-
n
|
1194
|
-
end
|
1195
|
-
|
1278
|
+
# If heartbeats are disabled, assume that TCP keepalives or a similar mechanism will be used
|
1279
|
+
# and disable socket read timeouts. See ruby-amqp/bunny#551.
|
1280
|
+
@transport.read_timeout = @heartbeat * 2.2
|
1281
|
+
@logger.debug { "Will use socket read timeout of #{@transport.read_timeout.to_i} seconds" }
|
1196
1282
|
|
1197
1283
|
# if there are existing channels we've just recovered from
|
1198
1284
|
# a network failure and need to fix the allocated set. See issue 205. MK.
|
1199
1285
|
if @channels.empty?
|
1286
|
+
@logger.debug { "Initializing channel ID allocator with channel_max = #{@channel_max}" }
|
1200
1287
|
@channel_id_allocator = ChannelIdAllocator.new(@channel_max)
|
1201
1288
|
end
|
1202
1289
|
|
@@ -1241,7 +1328,7 @@ module Bunny
|
|
1241
1328
|
end
|
1242
1329
|
|
1243
1330
|
if threaded?
|
1244
|
-
@
|
1331
|
+
@session_error_handler.raise(e)
|
1245
1332
|
else
|
1246
1333
|
raise e
|
1247
1334
|
end
|
@@ -1287,8 +1374,8 @@ module Bunny
|
|
1287
1374
|
@transport = Transport.new(self,
|
1288
1375
|
host_from_address(address),
|
1289
1376
|
port_from_address(address),
|
1290
|
-
@opts.merge(:
|
1291
|
-
|
1377
|
+
@opts.merge(:session_error_handler => @session_error_handler)
|
1378
|
+
)
|
1292
1379
|
|
1293
1380
|
# Reset the cached progname for the logger only when no logger was provided
|
1294
1381
|
@default_logger.progname = self.to_s
|