bunny 1.0.7 → 2.24.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/README.md +92 -87
- data/lib/amq/protocol/extensions.rb +2 -0
- data/lib/bunny/authentication/credentials_encoder.rb +2 -0
- data/lib/bunny/authentication/external_mechanism_encoder.rb +2 -0
- data/lib/bunny/authentication/plain_mechanism_encoder.rb +2 -0
- data/lib/bunny/channel.rb +485 -186
- data/lib/bunny/channel_id_allocator.rb +8 -4
- data/lib/bunny/concurrent/atomic_fixnum.rb +2 -0
- data/lib/bunny/concurrent/condition.rb +2 -0
- data/lib/bunny/concurrent/continuation_queue.rb +37 -13
- data/lib/bunny/concurrent/synchronized_sorted_set.rb +2 -0
- data/lib/bunny/consumer.rb +20 -13
- data/lib/bunny/consumer_tag_generator.rb +6 -2
- data/lib/bunny/consumer_work_pool.rb +37 -7
- data/lib/bunny/cruby/socket.rb +51 -22
- data/lib/bunny/cruby/ssl_socket.rb +68 -5
- data/lib/bunny/delivery_info.rb +3 -1
- data/lib/bunny/exceptions.rb +27 -4
- data/lib/bunny/exchange.rb +35 -29
- data/lib/bunny/framing.rb +2 -0
- data/lib/bunny/get_response.rb +85 -0
- data/lib/bunny/heartbeat_sender.rb +9 -6
- data/lib/bunny/message_properties.rb +2 -0
- data/lib/bunny/queue.rb +89 -41
- data/lib/bunny/reader_loop.rb +72 -28
- data/lib/bunny/return_info.rb +2 -0
- data/lib/bunny/session.rb +621 -225
- data/lib/bunny/socket.rb +7 -12
- data/lib/bunny/ssl_socket.rb +7 -12
- data/lib/bunny/test_kit.rb +15 -0
- data/lib/bunny/timeout.rb +3 -12
- data/lib/bunny/timestamp.rb +24 -0
- data/lib/bunny/transport.rb +223 -98
- data/lib/bunny/version.rb +2 -1
- data/lib/bunny/versioned_delivery_tag.rb +2 -0
- data/lib/bunny.rb +54 -8
- metadata +38 -224
- data/.gitignore +0 -22
- data/.rspec +0 -3
- data/.ruby-version +0 -1
- data/.travis.yml +0 -23
- data/.yardopts +0 -8
- data/ChangeLog.md +0 -1092
- data/Gemfile +0 -54
- data/LICENSE +0 -21
- 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.sh +0 -31
- data/bunny.gemspec +0 -40
- 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_server_named_queues.rb +0 -35
- data/examples/connection/channel_level_exception.rb +0 -35
- 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/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 -28
- 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 -20
- data/examples/guides/getting_started/weathr.rb +0 -47
- data/examples/guides/queues/one_off_consumer.rb +0 -23
- data/examples/guides/queues/redeliveries.rb +0 -79
- data/lib/bunny/compatibility.rb +0 -24
- data/lib/bunny/concurrent/linked_continuation_queue.rb +0 -61
- data/lib/bunny/jruby/socket.rb +0 -40
- data/lib/bunny/jruby/ssl_socket.rb +0 -53
- data/lib/bunny/system_timer.rb +0 -20
- data/profiling/basic_publish/with_4K_messages.rb +0 -33
- data/repl +0 -3
- 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_ack_spec.rb +0 -71
- data/spec/higher_level_api/integration/basic_cancel_spec.rb +0 -76
- data/spec/higher_level_api/integration/basic_consume_spec.rb +0 -225
- 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 -48
- data/spec/higher_level_api/integration/basic_nack_spec.rb +0 -79
- data/spec/higher_level_api/integration/basic_publish_spec.rb +0 -89
- data/spec/higher_level_api/integration/basic_qos_spec.rb +0 -29
- data/spec/higher_level_api/integration/basic_recover_spec.rb +0 -18
- data/spec/higher_level_api/integration/basic_reject_spec.rb +0 -74
- 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_flow_spec.rb +0 -21
- data/spec/higher_level_api/integration/channel_open_spec.rb +0 -57
- data/spec/higher_level_api/integration/confirm_select_spec.rb +0 -19
- data/spec/higher_level_api/integration/connection_spec.rb +0 -400
- data/spec/higher_level_api/integration/connection_stop_spec.rb +0 -26
- data/spec/higher_level_api/integration/consistent_hash_exchange_spec.rb +0 -50
- data/spec/higher_level_api/integration/consumer_cancellation_notification_spec.rb +0 -128
- data/spec/higher_level_api/integration/dead_lettering_spec.rb +0 -52
- data/spec/higher_level_api/integration/exchange_bind_spec.rb +0 -31
- data/spec/higher_level_api/integration/exchange_declare_spec.rb +0 -204
- 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 -31
- 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 -77
- data/spec/higher_level_api/integration/publishing_edge_cases_spec.rb +0 -65
- data/spec/higher_level_api/integration/queue_bind_spec.rb +0 -109
- data/spec/higher_level_api/integration/queue_declare_spec.rb +0 -190
- 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 -127
- 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 -44
- data/spec/issues/issue78_spec.rb +0 -75
- data/spec/issues/issue83_spec.rb +0 -31
- data/spec/issues/issue97_attachment.json +0 -1
- data/spec/issues/issue97_spec.rb +0 -176
- data/spec/lower_level_api/integration/basic_cancel_spec.rb +0 -69
- data/spec/lower_level_api/integration/basic_consume_spec.rb +0 -100
- data/spec/spec_helper.rb +0 -64
- data/spec/stress/channel_open_stress_spec.rb +0 -51
- data/spec/stress/channel_open_stress_with_single_threaded_connection_spec.rb +0 -28
- data/spec/stress/concurrent_consumers_stress_spec.rb +0 -69
- data/spec/stress/concurrent_publishers_stress_spec.rb +0 -57
- data/spec/stress/connection_open_close_spec.rb +0 -40
- 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/client_key.pem +0 -27
- data/spec/tls/server_cert.pem +0 -18
- data/spec/tls/server_key.pem +0 -27
- 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/system_timer_spec.rb +0 -10
- data/spec/unit/version_delivery_tag_spec.rb +0 -28
data/lib/bunny/session.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require "socket"
|
2
4
|
require "thread"
|
3
5
|
require "monitor"
|
@@ -10,11 +12,7 @@ require "bunny/authentication/credentials_encoder"
|
|
10
12
|
require "bunny/authentication/plain_mechanism_encoder"
|
11
13
|
require "bunny/authentication/external_mechanism_encoder"
|
12
14
|
|
13
|
-
|
14
|
-
require "bunny/concurrent/linked_continuation_queue"
|
15
|
-
else
|
16
|
-
require "bunny/concurrent/continuation_queue"
|
17
|
-
end
|
15
|
+
require "bunny/concurrent/continuation_queue"
|
18
16
|
|
19
17
|
require "amq/protocol/client"
|
20
18
|
require "amq/settings"
|
@@ -36,21 +34,17 @@ module Bunny
|
|
36
34
|
DEFAULT_HEARTBEAT = :server
|
37
35
|
# @private
|
38
36
|
DEFAULT_FRAME_MAX = 131072
|
39
|
-
#
|
37
|
+
# Hard limit the user cannot go over regardless of server configuration.
|
40
38
|
# @private
|
41
39
|
CHANNEL_MAX_LIMIT = 65535
|
42
|
-
DEFAULT_CHANNEL_MAX =
|
40
|
+
DEFAULT_CHANNEL_MAX = 2047
|
43
41
|
|
44
42
|
# backwards compatibility
|
45
43
|
# @private
|
46
44
|
CONNECT_TIMEOUT = Transport::DEFAULT_CONNECTION_TIMEOUT
|
47
45
|
|
48
46
|
# @private
|
49
|
-
DEFAULT_CONTINUATION_TIMEOUT =
|
50
|
-
8000
|
51
|
-
else
|
52
|
-
4000
|
53
|
-
end
|
47
|
+
DEFAULT_CONTINUATION_TIMEOUT = 15000
|
54
48
|
|
55
49
|
# RabbitMQ client metadata
|
56
50
|
DEFAULT_CLIENT_PROPERTIES = {
|
@@ -75,6 +69,7 @@ module Bunny
|
|
75
69
|
# Default reconnection interval for TCP connection failures
|
76
70
|
DEFAULT_NETWORK_RECOVERY_INTERVAL = 5.0
|
77
71
|
|
72
|
+
DEFAULT_RECOVERABLE_EXCEPTIONS = [StandardError, TCPConnectionFailedForAllHosts, TCPConnectionFailed, AMQ::Protocol::EmptyResponseError, SystemCallError, Timeout::Error, Bunny::ConnectionLevelException, Bunny::ConnectionClosedError]
|
78
73
|
|
79
74
|
#
|
80
75
|
# API
|
@@ -82,73 +77,121 @@ module Bunny
|
|
82
77
|
|
83
78
|
# @return [Bunny::Transport]
|
84
79
|
attr_reader :transport
|
85
|
-
attr_reader :status, :
|
80
|
+
attr_reader :status, :heartbeat, :user, :pass, :vhost, :frame_max, :channel_max, :threaded
|
86
81
|
attr_reader :server_capabilities, :server_properties, :server_authentication_mechanisms, :server_locales
|
87
|
-
attr_reader :default_channel
|
88
82
|
attr_reader :channel_id_allocator
|
89
83
|
# Authentication mechanism, e.g. "PLAIN" or "EXTERNAL"
|
90
84
|
# @return [String]
|
91
85
|
attr_reader :mechanism
|
92
86
|
# @return [Logger]
|
93
87
|
attr_reader :logger
|
94
|
-
# @return [Integer] Timeout for blocking protocol operations (queue.declare, queue.bind, etc), in milliseconds. Default is
|
88
|
+
# @return [Integer] Timeout for blocking protocol operations (queue.declare, queue.bind, etc), in milliseconds. Default is 15000.
|
95
89
|
attr_reader :continuation_timeout
|
96
|
-
|
90
|
+
attr_reader :network_recovery_interval
|
91
|
+
attr_reader :connection_name
|
92
|
+
attr_accessor :socket_configurator
|
93
|
+
attr_accessor :recoverable_exceptions
|
97
94
|
|
98
95
|
# @param [String, Hash] connection_string_or_opts Connection string or a hash of connection options
|
99
96
|
# @param [Hash] optz Extra options not related to connection
|
100
97
|
#
|
101
98
|
# @option connection_string_or_opts [String] :host ("127.0.0.1") Hostname or IP address to connect to
|
99
|
+
# @option connection_string_or_opts [Array<String>] :hosts (["127.0.0.1"]) list of hostname or IP addresses to select hostname from when connecting
|
100
|
+
# @option connection_string_or_opts [Array<String>] :addresses (["127.0.0.1:5672"]) list of addresses to select hostname and port from when connecting
|
102
101
|
# @option connection_string_or_opts [Integer] :port (5672) Port RabbitMQ listens on
|
103
102
|
# @option connection_string_or_opts [String] :username ("guest") Username
|
104
103
|
# @option connection_string_or_opts [String] :password ("guest") Password
|
105
104
|
# @option connection_string_or_opts [String] :vhost ("/") Virtual host to use
|
106
|
-
# @option connection_string_or_opts [Integer] :heartbeat (
|
105
|
+
# @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).
|
107
106
|
# @option connection_string_or_opts [Integer] :network_recovery_interval (4) Recovery interval periodic network recovery will use. This includes initial pause after network failure.
|
108
107
|
# @option connection_string_or_opts [Boolean] :tls (false) Should TLS/SSL be used?
|
109
108
|
# @option connection_string_or_opts [String] :tls_cert (nil) Path to client TLS/SSL certificate file (.pem)
|
110
109
|
# @option connection_string_or_opts [String] :tls_key (nil) Path to client TLS/SSL private key file (.pem)
|
111
110
|
# @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
|
112
|
-
# @option connection_string_or_opts [
|
111
|
+
# @option connection_string_or_opts [String] :verify_peer (true) Whether TLS peer verification should be performed
|
112
|
+
# @option connection_string_or_opts [Symbol] :tls_protocol (negotiated) What TLS version should be used (:TLSv1, :TLSv1_1, or :TLSv1_2)
|
113
|
+
# @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.
|
114
|
+
# @option connection_string_or_opts [Integer] :continuation_timeout (15000) Timeout for client operations that expect a response (e.g. {Bunny::Queue#get}), in milliseconds.
|
115
|
+
# @option connection_string_or_opts [Integer] :connection_timeout (30) Timeout in seconds for connecting to the server.
|
116
|
+
# @option connection_string_or_opts [Integer] :read_timeout (30) TCP socket read timeout in seconds. If heartbeats are disabled this will be ignored.
|
117
|
+
# @option connection_string_or_opts [Integer] :write_timeout (30) TCP socket write timeout in seconds.
|
118
|
+
# @option connection_string_or_opts [Proc] :hosts_shuffle_strategy a callable that reorders a list of host strings, defaults to Array#shuffle
|
119
|
+
# @option connection_string_or_opts [Proc] :recovery_completed a callable that will be called when a network recovery is performed
|
120
|
+
# @option connection_string_or_opts [Logger] :logger The logger. If missing, one is created using :log_file and :log_level.
|
121
|
+
# @option connection_string_or_opts [IO, String] :log_file The file or path to use when creating a logger. Defaults to STDOUT.
|
122
|
+
# @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.
|
123
|
+
# @option connection_string_or_opts [Integer] :log_level The log level to use when creating a logger. Defaults to LOGGER::WARN
|
124
|
+
# @option connection_string_or_opts [Boolean] :automatically_recover (true) Should automatically recover from network failures?
|
125
|
+
# @option connection_string_or_opts [Integer] :recovery_attempts (nil) Max number of recovery attempts, nil means forever
|
126
|
+
# @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.
|
127
|
+
# @option connection_string_or_opts [Proc] :recovery_attempt_started (nil) Will be called before every connection recovery attempt
|
128
|
+
# @option connection_string_or_opts [Proc] :recovery_completed (nil) Will be called after successful connection recovery
|
129
|
+
# @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
|
130
|
+
# @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)?
|
131
|
+
# @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.
|
113
132
|
#
|
114
133
|
# @option optz [String] :auth_mechanism ("PLAIN") Authentication mechanism, PLAIN or EXTERNAL
|
115
134
|
# @option optz [String] :locale ("PLAIN") Locale RabbitMQ should use
|
135
|
+
# @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.
|
116
136
|
#
|
117
137
|
# @see http://rubybunny.info/articles/connecting.html Connecting to RabbitMQ guide
|
118
138
|
# @see http://rubybunny.info/articles/tls.html TLS/SSL guide
|
119
139
|
# @api public
|
120
|
-
def initialize(connection_string_or_opts =
|
121
|
-
opts = case (
|
140
|
+
def initialize(connection_string_or_opts = ENV['RABBITMQ_URL'], optz = Hash.new)
|
141
|
+
opts = case (connection_string_or_opts)
|
122
142
|
when nil then
|
123
143
|
Hash.new
|
124
144
|
when String then
|
125
|
-
self.class.parse_uri(
|
145
|
+
self.class.parse_uri(connection_string_or_opts)
|
126
146
|
when Hash then
|
127
147
|
connection_string_or_opts
|
128
148
|
end.merge(optz)
|
129
149
|
|
150
|
+
@default_hosts_shuffle_strategy = Proc.new { |hosts| hosts.shuffle }
|
151
|
+
|
130
152
|
@opts = opts
|
131
|
-
|
132
|
-
|
153
|
+
log_file = opts[:log_file] || opts[:logfile] || STDOUT
|
154
|
+
log_level = opts[:log_level] || ENV["BUNNY_LOG_LEVEL"] || Logger::WARN
|
155
|
+
# we might need to log a warning about ill-formatted IPv6 address but
|
156
|
+
# progname includes hostname, so init like this first
|
157
|
+
@logger = opts.fetch(:logger, init_default_logger_without_progname(log_file, log_level))
|
158
|
+
|
159
|
+
@addresses = self.addresses_from(opts)
|
160
|
+
@address_index = 0
|
161
|
+
|
162
|
+
@transport = nil
|
133
163
|
@user = self.username_from(opts)
|
134
164
|
@pass = self.password_from(opts)
|
135
165
|
@vhost = self.vhost_from(opts)
|
136
|
-
@logfile = opts[:log_file] || opts[:logfile] || STDOUT
|
137
166
|
@threaded = opts.fetch(:threaded, true)
|
138
167
|
|
139
|
-
|
168
|
+
# re-init, see above
|
169
|
+
@logger = opts.fetch(:logger, init_default_logger(log_file, log_level))
|
170
|
+
|
171
|
+
validate_connection_options(opts)
|
172
|
+
@last_connection_error = nil
|
140
173
|
|
141
174
|
# should automatic recovery from network failures be used?
|
142
175
|
@automatically_recover = if opts[:automatically_recover].nil? && opts[:automatic_recovery].nil?
|
143
176
|
true
|
144
177
|
else
|
145
|
-
opts[:automatically_recover]
|
178
|
+
opts[:automatically_recover] | opts[:automatic_recovery]
|
146
179
|
end
|
180
|
+
@recovering_from_network_failure = false
|
181
|
+
@max_recovery_attempts = opts[:recovery_attempts]
|
182
|
+
@recovery_attempts = @max_recovery_attempts
|
183
|
+
# When this is set, connection attempts won't be reset after
|
184
|
+
# successful reconnection. Some find this behavior more sensible
|
185
|
+
# than the per-failure attempt counter. MK.
|
186
|
+
@reset_recovery_attempt_counter_after_reconnection = opts.fetch(:reset_recovery_attempts_after_reconnection, true)
|
187
|
+
|
147
188
|
@network_recovery_interval = opts.fetch(:network_recovery_interval, DEFAULT_NETWORK_RECOVERY_INTERVAL)
|
189
|
+
@recover_from_connection_close = opts.fetch(:recover_from_connection_close, true)
|
148
190
|
# in ms
|
149
|
-
@continuation_timeout
|
191
|
+
@continuation_timeout = opts.fetch(:continuation_timeout, DEFAULT_CONTINUATION_TIMEOUT)
|
150
192
|
|
151
193
|
@status = :not_connected
|
194
|
+
@manually_closed = false
|
152
195
|
@blocked = false
|
153
196
|
|
154
197
|
# these are negotiated with the broker during the connection tuning phase
|
@@ -156,10 +199,14 @@ module Bunny
|
|
156
199
|
@client_channel_max = normalize_client_channel_max(opts.fetch(:channel_max, DEFAULT_CHANNEL_MAX))
|
157
200
|
# will be-renegotiated during connection tuning steps. MK.
|
158
201
|
@channel_max = @client_channel_max
|
202
|
+
@heartbeat_sender = nil
|
159
203
|
@client_heartbeat = self.heartbeat_from(opts)
|
160
204
|
|
161
|
-
|
162
|
-
@
|
205
|
+
client_props = opts[:properties] || opts[:client_properties] || {}
|
206
|
+
@connection_name = client_props[:connection_name] || opts[:connection_name]
|
207
|
+
@client_properties = DEFAULT_CLIENT_PROPERTIES.merge(client_props)
|
208
|
+
.merge(connection_name: connection_name)
|
209
|
+
@mechanism = normalize_auth_mechanism(opts.fetch(:auth_mechanism, "PLAIN"))
|
163
210
|
@credentials_encoder = credentials_encoder_for(@mechanism)
|
164
211
|
@locale = @opts.fetch(:locale, DEFAULT_LOCALE)
|
165
212
|
|
@@ -170,12 +217,32 @@ module Bunny
|
|
170
217
|
# transport operations/continuations mutex. A workaround for
|
171
218
|
# the non-reentrant Ruby mutexes. MK.
|
172
219
|
@transport_mutex = @mutex_impl.new
|
220
|
+
@status_mutex = @mutex_impl.new
|
221
|
+
@address_index_mutex = @mutex_impl.new
|
222
|
+
|
173
223
|
@channels = Hash.new
|
174
224
|
|
175
|
-
@
|
225
|
+
@recovery_attempt_started = opts[:recovery_attempt_started]
|
226
|
+
@recovery_completed = opts[:recovery_completed]
|
227
|
+
@recovery_attempts_exhausted = opts[:recovery_attempts_exhausted]
|
228
|
+
|
229
|
+
@session_error_handler = opts.fetch(:session_error_handler, Thread.current)
|
230
|
+
|
231
|
+
@recoverable_exceptions = DEFAULT_RECOVERABLE_EXCEPTIONS.dup
|
176
232
|
|
177
233
|
self.reset_continuations
|
178
234
|
self.initialize_transport
|
235
|
+
|
236
|
+
end
|
237
|
+
|
238
|
+
def validate_connection_options(options)
|
239
|
+
if options[:hosts] && options[:addresses]
|
240
|
+
raise ArgumentError, "Connection options can't contain hosts and addresses at the same time"
|
241
|
+
end
|
242
|
+
|
243
|
+
if (options[:host] || options[:hostname]) && (options[:hosts] || options[:addresses])
|
244
|
+
@logger.warn "Connection options contain both a host and an array of hosts (addresses), please pick one."
|
245
|
+
end
|
179
246
|
end
|
180
247
|
|
181
248
|
# @return [String] RabbitMQ hostname (or IP address) used
|
@@ -187,9 +254,13 @@ module Bunny
|
|
187
254
|
# @return [String] Virtual host used
|
188
255
|
def virtual_host; self.vhost; end
|
189
256
|
|
190
|
-
# @
|
257
|
+
# @deprecated
|
258
|
+
# @return [Integer] Heartbeat timeout (not interval) used
|
191
259
|
def heartbeat_interval; self.heartbeat; end
|
192
260
|
|
261
|
+
# @return [Integer] Heartbeat timeout used
|
262
|
+
def heartbeat_timeout; self.heartbeat; end
|
263
|
+
|
193
264
|
# @return [Boolean] true if this connection uses TLS (SSL)
|
194
265
|
def uses_tls?
|
195
266
|
@transport.uses_tls?
|
@@ -207,6 +278,18 @@ module Bunny
|
|
207
278
|
@threaded
|
208
279
|
end
|
209
280
|
|
281
|
+
def host
|
282
|
+
@transport ? @transport.host : host_from_address(@addresses[@address_index])
|
283
|
+
end
|
284
|
+
|
285
|
+
def port
|
286
|
+
@transport ? @transport.port : port_from_address(@addresses[@address_index])
|
287
|
+
end
|
288
|
+
|
289
|
+
def reset_address_index
|
290
|
+
@address_index_mutex.synchronize { @address_index = 0 }
|
291
|
+
end
|
292
|
+
|
210
293
|
# @private
|
211
294
|
attr_reader :mutex_impl
|
212
295
|
|
@@ -218,6 +301,11 @@ module Bunny
|
|
218
301
|
@transport.configure_socket(&block)
|
219
302
|
end
|
220
303
|
|
304
|
+
# @return [Integer] Client socket port
|
305
|
+
def local_port
|
306
|
+
@transport.local_address.ip_port
|
307
|
+
end
|
308
|
+
|
221
309
|
# Starts the connection process.
|
222
310
|
#
|
223
311
|
# @see http://rubybunny.info/articles/getting_started.html
|
@@ -226,44 +314,60 @@ module Bunny
|
|
226
314
|
def start
|
227
315
|
return self if connected?
|
228
316
|
|
229
|
-
@status
|
317
|
+
@status_mutex.synchronize { @status = :connecting }
|
230
318
|
# reset here for cases when automatic network recovery kicks in
|
231
319
|
# when we were blocked. MK.
|
232
320
|
@blocked = false
|
233
321
|
self.reset_continuations
|
234
322
|
|
235
323
|
begin
|
236
|
-
|
237
|
-
|
238
|
-
|
324
|
+
begin
|
325
|
+
# close existing transport if we have one,
|
326
|
+
# to not leak sockets
|
327
|
+
@transport.maybe_initialize_socket
|
239
328
|
|
240
|
-
|
241
|
-
|
329
|
+
@transport.post_initialize_socket
|
330
|
+
@transport.connect
|
242
331
|
|
243
|
-
|
244
|
-
|
245
|
-
end
|
332
|
+
self.init_connection
|
333
|
+
self.open_connection
|
246
334
|
|
247
|
-
|
248
|
-
|
335
|
+
@reader_loop = nil
|
336
|
+
self.start_reader_loop if threaded?
|
249
337
|
|
250
|
-
|
251
|
-
|
338
|
+
rescue TCPConnectionFailed => e
|
339
|
+
@logger.warn e.message
|
340
|
+
self.initialize_transport
|
341
|
+
@logger.warn "Will try to connect to the next endpoint in line: #{@transport.host}:#{@transport.port}"
|
252
342
|
|
253
|
-
|
254
|
-
|
255
|
-
|
256
|
-
|
343
|
+
return self.start
|
344
|
+
rescue
|
345
|
+
@status_mutex.synchronize { @status = :not_connected }
|
346
|
+
raise
|
347
|
+
end
|
348
|
+
rescue HostListDepleted
|
349
|
+
self.reset_address_index
|
350
|
+
@status_mutex.synchronize { @status = :not_connected }
|
351
|
+
raise TCPConnectionFailedForAllHosts
|
257
352
|
end
|
353
|
+
@status_mutex.synchronize { @manually_closed = false }
|
258
354
|
|
259
355
|
self
|
260
356
|
end
|
261
357
|
|
262
|
-
|
358
|
+
def update_secret(value, reason)
|
359
|
+
@transport.send_frame(AMQ::Protocol::Connection::UpdateSecret.encode(value, reason))
|
360
|
+
@last_update_secret_ok = wait_on_continuations
|
361
|
+
raise_if_continuation_resulted_in_a_connection_error!
|
362
|
+
|
363
|
+
@last_update_secret_ok
|
364
|
+
end
|
365
|
+
|
366
|
+
# Socket operation write timeout used by this connection
|
263
367
|
# @return [Integer]
|
264
368
|
# @private
|
265
|
-
def
|
266
|
-
@transport.
|
369
|
+
def transport_write_timeout
|
370
|
+
@transport.write_timeout
|
267
371
|
end
|
268
372
|
|
269
373
|
# Opens a new channel and returns it. This method will block the calling
|
@@ -271,33 +375,44 @@ module Bunny
|
|
271
375
|
# opened (this operation is very fast and inexpensive).
|
272
376
|
#
|
273
377
|
# @return [Bunny::Channel] Newly opened channel
|
274
|
-
def create_channel(n = nil, consumer_pool_size = 1)
|
378
|
+
def create_channel(n = nil, consumer_pool_size = 1, consumer_pool_abort_on_exception = false, consumer_pool_shutdown_timeout = 60)
|
275
379
|
raise ArgumentError, "channel number 0 is reserved in the protocol and cannot be used" if 0 == n
|
380
|
+
raise ConnectionAlreadyClosed if manually_closed?
|
381
|
+
raise RuntimeError, "this connection is not open. Was Bunny::Session#start invoked? Is automatic recovery enabled?" if !connected?
|
276
382
|
|
277
|
-
|
278
|
-
ch
|
279
|
-
|
280
|
-
|
281
|
-
|
282
|
-
|
383
|
+
@channel_mutex.synchronize do
|
384
|
+
if n && (ch = @channels[n])
|
385
|
+
ch
|
386
|
+
else
|
387
|
+
ch = Bunny::Channel.new(self, n, ConsumerWorkPool.new(consumer_pool_size || 1, consumer_pool_abort_on_exception, consumer_pool_shutdown_timeout))
|
388
|
+
ch.open
|
389
|
+
ch
|
390
|
+
end
|
283
391
|
end
|
284
392
|
end
|
285
393
|
alias channel create_channel
|
286
394
|
|
287
395
|
# Closes the connection. This involves closing all of its channels.
|
288
|
-
def close
|
289
|
-
|
290
|
-
close_all_channels
|
396
|
+
def close(await_response = true)
|
397
|
+
@status_mutex.synchronize { @status = :closing }
|
291
398
|
|
292
|
-
|
293
|
-
|
294
|
-
|
399
|
+
ignoring_io_errors do
|
400
|
+
if @transport.open?
|
401
|
+
@logger.debug "Transport is still open..."
|
402
|
+
close_all_channels
|
295
403
|
|
296
|
-
|
297
|
-
|
404
|
+
@logger.debug "Will close all channels...."
|
405
|
+
self.close_connection(await_response)
|
406
|
+
end
|
298
407
|
|
408
|
+
clean_up_on_shutdown
|
409
|
+
end
|
410
|
+
@status_mutex.synchronize do
|
299
411
|
@status = :closed
|
412
|
+
@manually_closed = true
|
300
413
|
end
|
414
|
+
@logger.debug "Connection is closed"
|
415
|
+
true
|
301
416
|
end
|
302
417
|
alias stop close
|
303
418
|
|
@@ -321,14 +436,27 @@ module Bunny
|
|
321
436
|
status == :connecting
|
322
437
|
end
|
323
438
|
|
439
|
+
# @return [Boolean] true if this AMQP 0.9.1 connection is closing
|
440
|
+
# @api private
|
441
|
+
def closing?
|
442
|
+
@status_mutex.synchronize { @status == :closing }
|
443
|
+
end
|
444
|
+
|
324
445
|
# @return [Boolean] true if this AMQP 0.9.1 connection is closed
|
325
446
|
def closed?
|
326
|
-
status == :closed
|
447
|
+
@status_mutex.synchronize { @status == :closed }
|
448
|
+
end
|
449
|
+
|
450
|
+
# @return [Boolean] true if this AMQP 0.9.1 connection has been closed by the user (as opposed to the server)
|
451
|
+
def manually_closed?
|
452
|
+
@status_mutex.synchronize { @manually_closed == true }
|
327
453
|
end
|
328
454
|
|
329
455
|
# @return [Boolean] true if this AMQP 0.9.1 connection is open
|
330
456
|
def open?
|
331
|
-
|
457
|
+
@status_mutex.synchronize do
|
458
|
+
(status == :open || status == :connected || status == :connecting) && @transport.open?
|
459
|
+
end
|
332
460
|
end
|
333
461
|
alias connected? open?
|
334
462
|
|
@@ -337,40 +465,6 @@ module Bunny
|
|
337
465
|
@automatically_recover
|
338
466
|
end
|
339
467
|
|
340
|
-
#
|
341
|
-
# Backwards compatibility
|
342
|
-
#
|
343
|
-
|
344
|
-
# @private
|
345
|
-
def queue(*args)
|
346
|
-
@default_channel.queue(*args)
|
347
|
-
end
|
348
|
-
|
349
|
-
# @private
|
350
|
-
def direct(*args)
|
351
|
-
@default_channel.direct(*args)
|
352
|
-
end
|
353
|
-
|
354
|
-
# @private
|
355
|
-
def fanout(*args)
|
356
|
-
@default_channel.fanout(*args)
|
357
|
-
end
|
358
|
-
|
359
|
-
# @private
|
360
|
-
def topic(*args)
|
361
|
-
@default_channel.topic(*args)
|
362
|
-
end
|
363
|
-
|
364
|
-
# @private
|
365
|
-
def headers(*args)
|
366
|
-
@default_channel.headers(*args)
|
367
|
-
end
|
368
|
-
|
369
|
-
# @private
|
370
|
-
def exchange(*args)
|
371
|
-
@default_channel.exchange(*args)
|
372
|
-
end
|
373
|
-
|
374
468
|
# Defines a callback that will be executed when RabbitMQ blocks the connection
|
375
469
|
# because it is running low on memory or disk space (as configured via config file
|
376
470
|
# and/or rabbitmqctl).
|
@@ -404,7 +498,7 @@ module Bunny
|
|
404
498
|
# @param [String] uri amqp or amqps URI to parse
|
405
499
|
# @return [Hash] Parsed URI as a hash
|
406
500
|
def self.parse_uri(uri)
|
407
|
-
AMQ::Settings.
|
501
|
+
AMQ::Settings.configure(uri)
|
408
502
|
end
|
409
503
|
|
410
504
|
# Checks if a queue with given name exists.
|
@@ -420,6 +514,8 @@ module Bunny
|
|
420
514
|
begin
|
421
515
|
ch.queue(name, :passive => true)
|
422
516
|
true
|
517
|
+
rescue Bunny::ResourceLocked => _
|
518
|
+
true
|
423
519
|
rescue Bunny::NotFound => _
|
424
520
|
false
|
425
521
|
ensure
|
@@ -447,6 +543,24 @@ module Bunny
|
|
447
543
|
end
|
448
544
|
end
|
449
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
|
450
564
|
|
451
565
|
#
|
452
566
|
# Implementation
|
@@ -454,49 +568,69 @@ module Bunny
|
|
454
568
|
|
455
569
|
# @private
|
456
570
|
def open_channel(ch)
|
457
|
-
|
458
|
-
|
571
|
+
@channel_mutex.synchronize do
|
572
|
+
n = ch.number
|
573
|
+
self.register_channel(ch)
|
459
574
|
|
460
|
-
|
461
|
-
|
462
|
-
|
463
|
-
|
464
|
-
|
575
|
+
@transport_mutex.synchronize do
|
576
|
+
@transport.send_frame(AMQ::Protocol::Channel::Open.encode(n, AMQ::Protocol::EMPTY_STRING))
|
577
|
+
end
|
578
|
+
@last_channel_open_ok = wait_on_continuations
|
579
|
+
raise_if_continuation_resulted_in_a_connection_error!
|
465
580
|
|
466
|
-
|
581
|
+
@last_channel_open_ok
|
582
|
+
end
|
467
583
|
end
|
468
584
|
|
469
585
|
# @private
|
470
586
|
def close_channel(ch)
|
471
|
-
|
587
|
+
@channel_mutex.synchronize do
|
588
|
+
n = ch.number
|
472
589
|
|
473
|
-
|
474
|
-
|
475
|
-
|
590
|
+
@transport.send_frame(AMQ::Protocol::Channel::Close.encode(n, 200, "Goodbye", 0, 0))
|
591
|
+
@last_channel_close_ok = wait_on_continuations
|
592
|
+
raise_if_continuation_resulted_in_a_connection_error!
|
593
|
+
|
594
|
+
self.unregister_channel(ch)
|
595
|
+
self.release_channel_id(ch.id)
|
596
|
+
@last_channel_close_ok
|
597
|
+
end
|
598
|
+
end
|
476
599
|
|
477
|
-
|
478
|
-
|
600
|
+
# @private
|
601
|
+
def find_channel(number)
|
602
|
+
@channels[number]
|
603
|
+
end
|
604
|
+
|
605
|
+
# @private
|
606
|
+
def synchronised_find_channel(number)
|
607
|
+
@channel_mutex.synchronize { @channels[number] }
|
479
608
|
end
|
480
609
|
|
481
610
|
# @private
|
482
611
|
def close_all_channels
|
483
|
-
@
|
484
|
-
|
612
|
+
@channel_mutex.synchronize do
|
613
|
+
@channels.reject {|n, ch| n == 0 || !ch.open? }.each do |_, ch|
|
614
|
+
Bunny::Timeout.timeout(@transport.disconnect_timeout, ClientTimeout) { ch.close }
|
615
|
+
end
|
485
616
|
end
|
486
617
|
end
|
487
618
|
|
488
619
|
# @private
|
489
|
-
def close_connection(
|
620
|
+
def close_connection(await_response = true)
|
490
621
|
if @transport.open?
|
622
|
+
@logger.debug "Transport is still open"
|
491
623
|
@transport.send_frame(AMQ::Protocol::Connection::Close.encode(200, "Goodbye", 0, 0))
|
492
624
|
|
493
|
-
|
494
|
-
|
495
|
-
|
496
|
-
if sync
|
625
|
+
if await_response
|
626
|
+
@logger.debug "Waiting for a connection.close-ok..."
|
497
627
|
@last_connection_close_ok = wait_on_continuations
|
498
628
|
end
|
499
629
|
end
|
630
|
+
|
631
|
+
shut_down_all_consumer_work_pools!
|
632
|
+
maybe_shutdown_heartbeat_sender
|
633
|
+
@status_mutex.synchronize { @status = :not_connected }
|
500
634
|
end
|
501
635
|
|
502
636
|
# Handles incoming frames and dispatches them.
|
@@ -508,17 +642,20 @@ module Bunny
|
|
508
642
|
#
|
509
643
|
# @private
|
510
644
|
def handle_frame(ch_number, method)
|
511
|
-
@logger.debug "Session#handle_frame on #{ch_number}: #{method.inspect}"
|
645
|
+
@logger.debug { "Session#handle_frame on #{ch_number}: #{method.inspect}" }
|
512
646
|
case method
|
513
647
|
when AMQ::Protocol::Channel::OpenOk then
|
514
648
|
@continuations.push(method)
|
515
649
|
when AMQ::Protocol::Channel::CloseOk then
|
516
650
|
@continuations.push(method)
|
517
651
|
when AMQ::Protocol::Connection::Close then
|
518
|
-
|
519
|
-
|
520
|
-
|
521
|
-
|
652
|
+
if recover_from_connection_close?
|
653
|
+
@logger.warn "Recovering from connection.close (#{method.reply_text})"
|
654
|
+
clean_up_on_shutdown
|
655
|
+
handle_network_failure(instantiate_connection_level_exception(method))
|
656
|
+
else
|
657
|
+
clean_up_and_fail_on_connection_close!(method)
|
658
|
+
end
|
522
659
|
when AMQ::Protocol::Connection::CloseOk then
|
523
660
|
@last_connection_close_ok = method
|
524
661
|
begin
|
@@ -536,17 +673,28 @@ module Bunny
|
|
536
673
|
when AMQ::Protocol::Connection::Unblocked then
|
537
674
|
@blocked = false
|
538
675
|
@unblock_callback.call(method) if @unblock_callback
|
676
|
+
when AMQ::Protocol::Connection::UpdateSecretOk then
|
677
|
+
@continuations.push(method)
|
539
678
|
when AMQ::Protocol::Channel::Close then
|
540
679
|
begin
|
541
|
-
ch =
|
680
|
+
ch = synchronised_find_channel(ch_number)
|
681
|
+
# this includes sending a channel.close-ok and
|
682
|
+
# potentially invoking a user-provided callback,
|
683
|
+
# avoid doing that while holding a mutex lock. MK.
|
542
684
|
ch.handle_method(method)
|
543
685
|
ensure
|
544
|
-
|
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
|
545
692
|
end
|
546
693
|
when AMQ::Protocol::Basic::GetEmpty then
|
547
|
-
|
694
|
+
ch = find_channel(ch_number)
|
695
|
+
ch.handle_basic_get_empty(method)
|
548
696
|
else
|
549
|
-
if ch =
|
697
|
+
if ch = find_channel(ch_number)
|
550
698
|
ch.handle_method(method)
|
551
699
|
else
|
552
700
|
@logger.warn "Channel #{ch_number} is not open on this connection!"
|
@@ -575,32 +723,43 @@ module Bunny
|
|
575
723
|
end
|
576
724
|
end
|
577
725
|
|
726
|
+
# @private
|
727
|
+
def recover_from_connection_close?
|
728
|
+
@recover_from_connection_close
|
729
|
+
end
|
730
|
+
|
578
731
|
# @private
|
579
732
|
def handle_network_failure(exception)
|
580
733
|
raise NetworkErrorWrapper.new(exception) unless @threaded
|
581
734
|
|
582
|
-
@status = :disconnected
|
735
|
+
@status_mutex.synchronize { @status = :disconnected }
|
583
736
|
|
584
737
|
if !recovering_from_network_failure?
|
585
|
-
|
586
|
-
|
587
|
-
|
588
|
-
|
589
|
-
|
590
|
-
|
591
|
-
|
738
|
+
begin
|
739
|
+
@recovering_from_network_failure = true
|
740
|
+
if recoverable_network_failure?(exception)
|
741
|
+
announce_network_failure_recovery
|
742
|
+
@channel_mutex.synchronize do
|
743
|
+
@channels.each do |n, ch|
|
744
|
+
ch.maybe_kill_consumer_work_pool!
|
745
|
+
end
|
746
|
+
end
|
747
|
+
@reader_loop.stop if @reader_loop
|
748
|
+
maybe_shutdown_heartbeat_sender
|
592
749
|
|
593
|
-
|
594
|
-
|
595
|
-
|
750
|
+
recover_from_network_failure
|
751
|
+
else
|
752
|
+
@logger.error "Exception #{exception.message} is considered unrecoverable..."
|
753
|
+
end
|
754
|
+
ensure
|
755
|
+
@recovering_from_network_failure = false
|
596
756
|
end
|
597
757
|
end
|
598
758
|
end
|
599
759
|
|
600
760
|
# @private
|
601
761
|
def recoverable_network_failure?(exception)
|
602
|
-
|
603
|
-
true
|
762
|
+
@recoverable_exceptions.any? {|x| exception.kind_of? x}
|
604
763
|
end
|
605
764
|
|
606
765
|
# @private
|
@@ -608,38 +767,112 @@ module Bunny
|
|
608
767
|
@recovering_from_network_failure
|
609
768
|
end
|
610
769
|
|
770
|
+
# @private
|
771
|
+
def announce_network_failure_recovery
|
772
|
+
if recovery_attempts_limited?
|
773
|
+
@logger.warn "Will recover from a network failure (#{@recovery_attempts} out of #{@max_recovery_attempts} left)..."
|
774
|
+
else
|
775
|
+
@logger.warn "Will recover from a network failure (no retry limit)..."
|
776
|
+
end
|
777
|
+
end
|
778
|
+
|
611
779
|
# @private
|
612
780
|
def recover_from_network_failure
|
613
|
-
|
614
|
-
|
615
|
-
|
616
|
-
self.initialize_transport
|
617
|
-
self.start
|
781
|
+
sleep @network_recovery_interval
|
782
|
+
@logger.debug "Will attempt connection recovery..."
|
783
|
+
notify_of_recovery_attempt_start
|
618
784
|
|
619
|
-
|
620
|
-
@recovering_from_network_failure = false
|
785
|
+
self.initialize_transport
|
621
786
|
|
622
|
-
|
787
|
+
@logger.warn "Retrying connection on next host in line: #{@transport.host}:#{@transport.port}"
|
788
|
+
self.start
|
789
|
+
|
790
|
+
if open?
|
791
|
+
|
792
|
+
@recovering_from_network_failure = false
|
793
|
+
@logger.debug "Connection is now open"
|
794
|
+
if @reset_recovery_attempt_counter_after_reconnection
|
795
|
+
@logger.debug "Resetting recovery attempt counter after successful reconnection"
|
796
|
+
reset_recovery_attempt_counter!
|
797
|
+
else
|
798
|
+
@logger.debug "Not resetting recovery attempt counter after successful reconnection, as configured"
|
799
|
+
end
|
800
|
+
|
801
|
+
recover_channels
|
802
|
+
notify_of_recovery_completion
|
803
|
+
end
|
804
|
+
rescue HostListDepleted
|
805
|
+
reset_address_index
|
806
|
+
retry
|
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!
|
813
|
+
announce_network_failure_recovery
|
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
|
623
821
|
end
|
624
|
-
|
625
|
-
|
626
|
-
sleep @network_recovery_interval
|
627
|
-
retry if recoverable_network_failure?(e)
|
822
|
+
else
|
823
|
+
raise e
|
628
824
|
end
|
629
825
|
end
|
630
826
|
|
631
827
|
# @private
|
632
|
-
def
|
633
|
-
|
634
|
-
|
635
|
-
|
636
|
-
|
637
|
-
|
828
|
+
def recovery_attempts_limited?
|
829
|
+
!!@max_recovery_attempts
|
830
|
+
end
|
831
|
+
|
832
|
+
# @private
|
833
|
+
def should_retry_recovery?
|
834
|
+
!recovery_attempts_limited? || @recovery_attempts > 1
|
835
|
+
end
|
836
|
+
|
837
|
+
# @private
|
838
|
+
def decrement_recovery_attemp_counter!
|
839
|
+
if @recovery_attempts
|
840
|
+
@recovery_attempts -= 1
|
841
|
+
@logger.debug "#{@recovery_attempts} recovery attempts left"
|
842
|
+
end
|
843
|
+
@recovery_attempts
|
844
|
+
end
|
845
|
+
|
846
|
+
# @private
|
847
|
+
def reset_recovery_attempt_counter!
|
848
|
+
@recovery_attempts = @max_recovery_attempts
|
849
|
+
end
|
638
850
|
|
639
|
-
|
851
|
+
# @private
|
852
|
+
def recover_channels
|
853
|
+
@channel_mutex.synchronize do
|
854
|
+
@channels.each do |n, ch|
|
855
|
+
ch.open
|
856
|
+
ch.recover_from_network_failure
|
857
|
+
end
|
640
858
|
end
|
641
859
|
end
|
642
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
|
+
|
643
876
|
# @private
|
644
877
|
def instantiate_connection_level_exception(frame)
|
645
878
|
case frame
|
@@ -669,9 +902,45 @@ module Bunny
|
|
669
902
|
end
|
670
903
|
end
|
671
904
|
|
905
|
+
def clean_up_and_fail_on_connection_close!(method)
|
906
|
+
@last_connection_error = instantiate_connection_level_exception(method)
|
907
|
+
@continuations.push(method)
|
908
|
+
|
909
|
+
clean_up_on_shutdown
|
910
|
+
if threaded?
|
911
|
+
@session_error_handler.raise(@last_connection_error)
|
912
|
+
else
|
913
|
+
raise @last_connection_error
|
914
|
+
end
|
915
|
+
end
|
916
|
+
|
917
|
+
def clean_up_on_shutdown
|
918
|
+
begin
|
919
|
+
shut_down_all_consumer_work_pools!
|
920
|
+
maybe_shutdown_reader_loop
|
921
|
+
maybe_shutdown_heartbeat_sender
|
922
|
+
rescue ShutdownSignal => _sse
|
923
|
+
# no-op
|
924
|
+
rescue Exception => e
|
925
|
+
@logger.warn "Caught an exception when cleaning up after receiving connection.close: #{e.message}"
|
926
|
+
ensure
|
927
|
+
close_transport
|
928
|
+
end
|
929
|
+
end
|
930
|
+
|
672
931
|
# @private
|
673
|
-
def
|
674
|
-
|
932
|
+
def addresses_from(options)
|
933
|
+
shuffle_strategy = options.fetch(:hosts_shuffle_strategy, @default_hosts_shuffle_strategy)
|
934
|
+
|
935
|
+
addresses = options[:host] || options[:hostname] || options[:addresses] ||
|
936
|
+
options[:hosts] || ["#{DEFAULT_HOST}:#{port_from(options)}"]
|
937
|
+
addresses = [addresses] unless addresses.is_a? Array
|
938
|
+
|
939
|
+
addrs = addresses.map do |address|
|
940
|
+
host_with_port?(address) ? address : "#{address}:#{port_from(@opts)}"
|
941
|
+
end
|
942
|
+
|
943
|
+
shuffle_strategy.call(addrs)
|
675
944
|
end
|
676
945
|
|
677
946
|
# @private
|
@@ -685,6 +954,63 @@ module Bunny
|
|
685
954
|
options.fetch(:port, fallback)
|
686
955
|
end
|
687
956
|
|
957
|
+
# @private
|
958
|
+
def host_with_port?(address)
|
959
|
+
# we need to handle cases such as [2001:db8:85a3:8d3:1319:8a2e:370:7348]:5671
|
960
|
+
last_colon = address.rindex(":")
|
961
|
+
last_closing_square_bracket = address.rindex("]")
|
962
|
+
|
963
|
+
if last_closing_square_bracket.nil?
|
964
|
+
address.include?(":")
|
965
|
+
else
|
966
|
+
last_closing_square_bracket < last_colon
|
967
|
+
end
|
968
|
+
end
|
969
|
+
|
970
|
+
# @private
|
971
|
+
def host_from_address(address)
|
972
|
+
# we need to handle cases such as [2001:db8:85a3:8d3:1319:8a2e:370:7348]:5671
|
973
|
+
last_colon = address.rindex(":")
|
974
|
+
last_closing_square_bracket = address.rindex("]")
|
975
|
+
|
976
|
+
if last_closing_square_bracket.nil?
|
977
|
+
parts = address.split(":")
|
978
|
+
# this looks like an unquoted IPv6 address, so emit a warning
|
979
|
+
if parts.size > 2
|
980
|
+
@logger.warn "Address #{address} looks like an unquoted IPv6 address. Make sure you quote IPv6 addresses like so: [2001:db8:85a3:8d3:1319:8a2e:370:7348]"
|
981
|
+
end
|
982
|
+
return parts[0]
|
983
|
+
end
|
984
|
+
|
985
|
+
if last_closing_square_bracket < last_colon
|
986
|
+
# there is a port
|
987
|
+
address[0, last_colon]
|
988
|
+
elsif last_closing_square_bracket > last_colon
|
989
|
+
address
|
990
|
+
end
|
991
|
+
end
|
992
|
+
|
993
|
+
# @private
|
994
|
+
def port_from_address(address)
|
995
|
+
# we need to handle cases such as [2001:db8:85a3:8d3:1319:8a2e:370:7348]:5671
|
996
|
+
last_colon = address.rindex(":")
|
997
|
+
last_closing_square_bracket = address.rindex("]")
|
998
|
+
|
999
|
+
if last_closing_square_bracket.nil?
|
1000
|
+
parts = address.split(":")
|
1001
|
+
# this looks like an unquoted IPv6 address, so emit a warning
|
1002
|
+
if parts.size > 2
|
1003
|
+
@logger.warn "Address #{address} looks like an unquoted IPv6 address. Make sure you quote IPv6 addresses like so: [2001:db8:85a3:8d3:1319:8a2e:370:7348]"
|
1004
|
+
end
|
1005
|
+
return parts[1].to_i
|
1006
|
+
end
|
1007
|
+
|
1008
|
+
if last_closing_square_bracket < last_colon
|
1009
|
+
# there is a port
|
1010
|
+
address[(last_colon + 1)..-1].to_i
|
1011
|
+
end
|
1012
|
+
end
|
1013
|
+
|
688
1014
|
# @private
|
689
1015
|
def vhost_from(options)
|
690
1016
|
options[:virtual_host] || options[:vhost] || DEFAULT_VHOST
|
@@ -702,7 +1028,7 @@ module Bunny
|
|
702
1028
|
|
703
1029
|
# @private
|
704
1030
|
def heartbeat_from(options)
|
705
|
-
options[:heartbeat] || options[:
|
1031
|
+
options[:heartbeat] || options[:heartbeat_timeout] || options[:requested_heartbeat] || options[:heartbeat_interval] || DEFAULT_HEARTBEAT
|
706
1032
|
end
|
707
1033
|
|
708
1034
|
# @private
|
@@ -739,7 +1065,7 @@ module Bunny
|
|
739
1065
|
|
740
1066
|
# @private
|
741
1067
|
def reader_loop
|
742
|
-
@reader_loop ||= ReaderLoop.new(@transport, self,
|
1068
|
+
@reader_loop ||= ReaderLoop.new(@transport, self, @session_error_handler)
|
743
1069
|
end
|
744
1070
|
|
745
1071
|
# @private
|
@@ -749,17 +1075,8 @@ module Bunny
|
|
749
1075
|
if threaded?
|
750
1076
|
# this is the easiest way to wait until the loop
|
751
1077
|
# is guaranteed to have terminated
|
752
|
-
@reader_loop.
|
753
|
-
|
754
|
-
# on JRuby because sun.nio.ch.KQueueArrayWrapper#kevent0 is
|
755
|
-
# a native method that cannot be (easily) interrupted.
|
756
|
-
# So we use this ugly hack or else our test suite takes forever
|
757
|
-
# to run on JRuby (a new connection is opened/closed per example). MK.
|
758
|
-
if defined?(JRUBY_VERSION)
|
759
|
-
sleep 0.075
|
760
|
-
else
|
761
|
-
@reader_loop.join
|
762
|
-
end
|
1078
|
+
@reader_loop.terminate_with(ShutdownSignal)
|
1079
|
+
@reader_loop.join
|
763
1080
|
else
|
764
1081
|
# single threaded mode, nothing to do. MK.
|
765
1082
|
end
|
@@ -793,6 +1110,9 @@ module Bunny
|
|
793
1110
|
# @private
|
794
1111
|
def send_frame(frame, signal_activity = true)
|
795
1112
|
if open?
|
1113
|
+
# @transport_mutex.synchronize do
|
1114
|
+
# @transport.write(frame.encode)
|
1115
|
+
# end
|
796
1116
|
@transport.write(frame.encode)
|
797
1117
|
signal_activity! if signal_activity
|
798
1118
|
else
|
@@ -815,7 +1135,7 @@ module Bunny
|
|
815
1135
|
end
|
816
1136
|
end
|
817
1137
|
|
818
|
-
# Sends multiple frames, one
|
1138
|
+
# Sends multiple frames, in one go. For thread safety this method takes a channel
|
819
1139
|
# object and synchronizes on it.
|
820
1140
|
#
|
821
1141
|
# @private
|
@@ -824,10 +1144,18 @@ module Bunny
|
|
824
1144
|
# threads publish on the same channel aggressively, at some point frames will be
|
825
1145
|
# delivered out of order and broker will raise 505 UNEXPECTED_FRAME exception.
|
826
1146
|
# If we synchronize on the channel, however, this is both thread safe and pretty fine-grained
|
827
|
-
# locking. Note that "single frame" methods do not need this kind of synchronization
|
1147
|
+
# locking. Note that "single frame" methods technically do not need this kind of synchronization
|
1148
|
+
# (no incorrect frame interleaving of the same kind as with basic.publish isn't possible) but we
|
1149
|
+
# still recommend not sharing channels between threads except for consumer-only cases in the docs. MK.
|
828
1150
|
channel.synchronize do
|
829
|
-
|
830
|
-
|
1151
|
+
# see rabbitmq/rabbitmq-server#156
|
1152
|
+
if open?
|
1153
|
+
data = frames.reduce(+"") { |acc, frame| acc << frame.encode }
|
1154
|
+
@transport.write(data)
|
1155
|
+
signal_activity!
|
1156
|
+
else
|
1157
|
+
raise ConnectionClosedError.new(frames)
|
1158
|
+
end
|
831
1159
|
end
|
832
1160
|
end # send_frameset(frames)
|
833
1161
|
|
@@ -841,10 +1169,14 @@ module Bunny
|
|
841
1169
|
# threads publish on the same channel aggressively, at some point frames will be
|
842
1170
|
# delivered out of order and broker will raise 505 UNEXPECTED_FRAME exception.
|
843
1171
|
# If we synchronize on the channel, however, this is both thread safe and pretty fine-grained
|
844
|
-
# locking.
|
1172
|
+
# locking. See a note about "single frame" methods in a comment in `send_frameset`. MK.
|
845
1173
|
channel.synchronize do
|
846
|
-
|
847
|
-
|
1174
|
+
if open?
|
1175
|
+
frames.each { |frame| self.send_frame_without_timeout(frame, false) }
|
1176
|
+
signal_activity!
|
1177
|
+
else
|
1178
|
+
raise ConnectionClosedError.new(frames)
|
1179
|
+
end
|
848
1180
|
end
|
849
1181
|
end # send_frameset_without_timeout(frames)
|
850
1182
|
|
@@ -864,7 +1196,12 @@ module Bunny
|
|
864
1196
|
# @return [String]
|
865
1197
|
# @api public
|
866
1198
|
def to_s
|
867
|
-
"
|
1199
|
+
oid = ("0x%x" % (self.object_id << 1))
|
1200
|
+
"#<#{self.class.name}:#{oid} #{@user}@#{host}:#{port}, vhost=#{@vhost}, addresses=[#{@addresses.join(',')}]>"
|
1201
|
+
end
|
1202
|
+
|
1203
|
+
def inspect
|
1204
|
+
to_s
|
868
1205
|
end
|
869
1206
|
|
870
1207
|
protected
|
@@ -881,7 +1218,7 @@ module Bunny
|
|
881
1218
|
@server_authentication_mechanisms = (connection_start.mechanisms || "").split(" ")
|
882
1219
|
@server_locales = Array(connection_start.locales)
|
883
1220
|
|
884
|
-
@status = :connected
|
1221
|
+
@status_mutex.synchronize { @status = :connected }
|
885
1222
|
end
|
886
1223
|
|
887
1224
|
# @private
|
@@ -890,16 +1227,18 @@ module Bunny
|
|
890
1227
|
@logger.debug "Sent connection.start-ok"
|
891
1228
|
|
892
1229
|
frame = begin
|
893
|
-
@transport.read_next_frame
|
1230
|
+
fr = @transport.read_next_frame
|
1231
|
+
while fr.is_a?(AMQ::Protocol::HeartbeatFrame)
|
1232
|
+
fr = @transport.read_next_frame
|
1233
|
+
end
|
1234
|
+
fr
|
894
1235
|
# frame timeout means the broker has closed the TCP connection, which it
|
895
1236
|
# does per 0.9.1 spec.
|
896
|
-
rescue
|
1237
|
+
rescue
|
897
1238
|
nil
|
898
1239
|
end
|
899
1240
|
if frame.nil?
|
900
|
-
|
901
|
-
@logger.error "RabbitMQ closed TCP connection before AMQP 0.9.1 connection was finalized. Most likely this means authentication failure."
|
902
|
-
raise Bunny::PossibleAuthenticationFailureError.new(self.user, self.vhost, self.password.size)
|
1241
|
+
raise TCPConnectionFailed.new('An empty frame was received while opening the connection. In RabbitMQ <= 3.1 this could mean an authentication issue.')
|
903
1242
|
end
|
904
1243
|
|
905
1244
|
response = frame.decode_payload
|
@@ -921,31 +1260,46 @@ module Bunny
|
|
921
1260
|
else
|
922
1261
|
negotiate_value(@client_heartbeat, connection_tune.heartbeat)
|
923
1262
|
end
|
924
|
-
@logger.debug "Heartbeat interval negotiation: client = #{@client_heartbeat}, server = #{connection_tune.heartbeat}, result = #{@heartbeat}"
|
925
|
-
@logger.
|
926
|
-
|
927
|
-
|
1263
|
+
@logger.debug { "Heartbeat interval negotiation: client = #{@client_heartbeat}, server = #{connection_tune.heartbeat}, result = #{@heartbeat}" }
|
1264
|
+
@logger.debug "Heartbeat interval used (in seconds): #{@heartbeat}"
|
1265
|
+
|
1266
|
+
# We set the read_write_timeout to twice the heartbeat value,
|
1267
|
+
# and then some padding for edge cases.
|
1268
|
+
# This allows us to miss a single heartbeat before we time out the socket.
|
1269
|
+
# If heartbeats are disabled, assume that TCP keepalives or a similar mechanism will be used
|
1270
|
+
# and disable socket read timeouts. See ruby-amqp/bunny#551.
|
1271
|
+
@transport.read_timeout = @heartbeat * 2.2
|
1272
|
+
@logger.debug { "Will use socket read timeout of #{@transport.read_timeout.to_i} seconds" }
|
1273
|
+
|
1274
|
+
# if there are existing channels we've just recovered from
|
1275
|
+
# a network failure and need to fix the allocated set. See issue 205. MK.
|
1276
|
+
if @channels.empty?
|
1277
|
+
@logger.debug { "Initializing channel ID allocator with channel_max = #{@channel_max}" }
|
1278
|
+
@channel_id_allocator = ChannelIdAllocator.new(@channel_max)
|
1279
|
+
end
|
928
1280
|
|
929
1281
|
@transport.send_frame(AMQ::Protocol::Connection::TuneOk.encode(@channel_max, @frame_max, @heartbeat))
|
930
|
-
@logger.debug "Sent connection.tune-ok with heartbeat interval = #{@heartbeat}, frame_max = #{@frame_max}, channel_max = #{@channel_max}"
|
1282
|
+
@logger.debug { "Sent connection.tune-ok with heartbeat interval = #{@heartbeat}, frame_max = #{@frame_max}, channel_max = #{@channel_max}" }
|
931
1283
|
@transport.send_frame(AMQ::Protocol::Connection::Open.encode(self.vhost))
|
932
|
-
@logger.debug "Sent connection.open with vhost = #{self.vhost}"
|
1284
|
+
@logger.debug { "Sent connection.open with vhost = #{self.vhost}" }
|
933
1285
|
|
934
1286
|
frame2 = begin
|
935
|
-
@transport.read_next_frame
|
1287
|
+
fr = @transport.read_next_frame
|
1288
|
+
while fr.is_a?(AMQ::Protocol::HeartbeatFrame)
|
1289
|
+
fr = @transport.read_next_frame
|
1290
|
+
end
|
1291
|
+
fr
|
936
1292
|
# frame timeout means the broker has closed the TCP connection, which it
|
937
1293
|
# does per 0.9.1 spec.
|
938
|
-
rescue
|
1294
|
+
rescue
|
939
1295
|
nil
|
940
1296
|
end
|
941
1297
|
if frame2.nil?
|
942
|
-
|
943
|
-
@logger.warn "RabbitMQ closed TCP connection before AMQP 0.9.1 connection was finalized. Most likely this means authentication failure."
|
944
|
-
raise Bunny::PossibleAuthenticationFailureError.new(self.user, self.vhost, self.password.size)
|
1298
|
+
raise TCPConnectionFailed.new('An empty frame was received while opening the connection. In RabbitMQ <= 3.1 this could mean an authentication issue.')
|
945
1299
|
end
|
946
1300
|
connection_open_ok = frame2.decode_payload
|
947
1301
|
|
948
|
-
@status = :open
|
1302
|
+
@status_mutex.synchronize { @status = :open }
|
949
1303
|
if @heartbeat && @heartbeat > 0
|
950
1304
|
initialize_heartbeat_sender
|
951
1305
|
end
|
@@ -956,7 +1310,7 @@ module Bunny
|
|
956
1310
|
begin
|
957
1311
|
shut_down_all_consumer_work_pools!
|
958
1312
|
maybe_shutdown_reader_loop
|
959
|
-
rescue ShutdownSignal =>
|
1313
|
+
rescue ShutdownSignal => _sse
|
960
1314
|
# no-op
|
961
1315
|
rescue Exception => e
|
962
1316
|
@logger.warn "Caught an exception when cleaning up after receiving connection.close: #{e.message}"
|
@@ -964,7 +1318,11 @@ module Bunny
|
|
964
1318
|
close_transport
|
965
1319
|
end
|
966
1320
|
|
967
|
-
|
1321
|
+
if threaded?
|
1322
|
+
@session_error_handler.raise(e)
|
1323
|
+
else
|
1324
|
+
raise e
|
1325
|
+
end
|
968
1326
|
else
|
969
1327
|
raise "could not open connection: server did not respond with connection.open-ok but #{connection_open_ok.inspect} instead"
|
970
1328
|
end
|
@@ -977,7 +1335,7 @@ module Bunny
|
|
977
1335
|
|
978
1336
|
# @private
|
979
1337
|
def negotiate_value(client_value, server_value)
|
980
|
-
return server_value if
|
1338
|
+
return server_value if [:server, "server"].include?(client_value)
|
981
1339
|
|
982
1340
|
if client_value == 0 || server_value == 0
|
983
1341
|
[client_value, server_value].max
|
@@ -1001,7 +1359,22 @@ module Bunny
|
|
1001
1359
|
|
1002
1360
|
# @private
|
1003
1361
|
def initialize_transport
|
1004
|
-
|
1362
|
+
if address = @addresses[ @address_index ]
|
1363
|
+
@address_index_mutex.synchronize { @address_index += 1 }
|
1364
|
+
@transport.close rescue nil # Let's make sure the previous transport socket is closed
|
1365
|
+
@transport = Transport.new(self,
|
1366
|
+
host_from_address(address),
|
1367
|
+
port_from_address(address),
|
1368
|
+
@opts.merge(:session_error_handler => @session_error_handler)
|
1369
|
+
)
|
1370
|
+
|
1371
|
+
# Reset the cached progname for the logger only when no logger was provided
|
1372
|
+
@default_logger.progname = self.to_s
|
1373
|
+
|
1374
|
+
@transport
|
1375
|
+
else
|
1376
|
+
raise HostListDepleted
|
1377
|
+
end
|
1005
1378
|
end
|
1006
1379
|
|
1007
1380
|
# @private
|
@@ -1027,16 +1400,9 @@ module Bunny
|
|
1027
1400
|
Authentication::CredentialsEncoder.for_session(self)
|
1028
1401
|
end
|
1029
1402
|
|
1030
|
-
|
1031
|
-
|
1032
|
-
|
1033
|
-
@continuations = Concurrent::LinkedContinuationQueue.new
|
1034
|
-
end
|
1035
|
-
else
|
1036
|
-
# @private
|
1037
|
-
def reset_continuations
|
1038
|
-
@continuations = Concurrent::ContinuationQueue.new
|
1039
|
-
end
|
1403
|
+
# @private
|
1404
|
+
def reset_continuations
|
1405
|
+
@continuations = Concurrent::ContinuationQueue.new
|
1040
1406
|
end
|
1041
1407
|
|
1042
1408
|
# @private
|
@@ -1049,12 +1415,22 @@ module Bunny
|
|
1049
1415
|
end
|
1050
1416
|
|
1051
1417
|
# @private
|
1052
|
-
def
|
1053
|
-
@
|
1054
|
-
|
1055
|
-
|
1418
|
+
def init_default_logger(logfile, level)
|
1419
|
+
@default_logger = begin
|
1420
|
+
lgr = ::Logger.new(logfile)
|
1421
|
+
lgr.level = normalize_log_level(level)
|
1422
|
+
lgr.progname = self.to_s
|
1423
|
+
lgr
|
1424
|
+
end
|
1425
|
+
end
|
1056
1426
|
|
1057
|
-
|
1427
|
+
# @private
|
1428
|
+
def init_default_logger_without_progname(logfile, level)
|
1429
|
+
@default_logger = begin
|
1430
|
+
lgr = ::Logger.new(logfile)
|
1431
|
+
lgr.level = normalize_log_level(level)
|
1432
|
+
lgr
|
1433
|
+
end
|
1058
1434
|
end
|
1059
1435
|
|
1060
1436
|
# @private
|
@@ -1078,6 +1454,7 @@ module Bunny
|
|
1078
1454
|
end
|
1079
1455
|
|
1080
1456
|
def normalize_client_channel_max(n)
|
1457
|
+
return CHANNEL_MAX_LIMIT if n.nil?
|
1081
1458
|
return CHANNEL_MAX_LIMIT if n > CHANNEL_MAX_LIMIT
|
1082
1459
|
|
1083
1460
|
case n
|
@@ -1087,6 +1464,25 @@ module Bunny
|
|
1087
1464
|
n
|
1088
1465
|
end
|
1089
1466
|
end
|
1467
|
+
|
1468
|
+
def normalize_auth_mechanism(value)
|
1469
|
+
case value
|
1470
|
+
when [] then
|
1471
|
+
"PLAIN"
|
1472
|
+
when nil then
|
1473
|
+
"PLAIN"
|
1474
|
+
else
|
1475
|
+
value
|
1476
|
+
end
|
1477
|
+
end
|
1478
|
+
|
1479
|
+
def ignoring_io_errors(&block)
|
1480
|
+
begin
|
1481
|
+
block.call
|
1482
|
+
rescue AMQ::Protocol::EmptyResponseError, IOError, SystemCallError, Bunny::NetworkFailure => _
|
1483
|
+
# ignore
|
1484
|
+
end
|
1485
|
+
end
|
1090
1486
|
end # Session
|
1091
1487
|
|
1092
1488
|
# backwards compatibility
|