5
5
import errno
6
6
import logging
7
7
import io
8
- from random import shuffle
8
+ from random import shuffle , uniform
9
9
import socket
10
10
import time
11
11
import traceback
@@ -78,6 +78,14 @@ class BrokerConnection(object):
78
78
reconnect_backoff_ms (int): The amount of time in milliseconds to
79
79
wait before attempting to reconnect to a given host.
80
80
Default: 50.
81
+ reconnect_backoff_max_ms (int): The maximum amount of time in
82
+ milliseconds to wait when reconnecting to a broker that has
83
+ repeatedly failed to connect. If provided, the backoff per host
84
+ will increase exponentially for each consecutive connection
85
+ failure, up to this maximum. To avoid connection storms, a
86
+ randomization factor of 0.2 will be applied to the backoff
87
+ resulting in a random range between 20% below and 20% above
88
+ the computed value. Default: 1000.
81
89
request_timeout_ms (int): Client request timeout in milliseconds.
82
90
Default: 40000.
83
91
max_in_flight_requests_per_connection (int): Requests are pipelined
@@ -140,6 +148,7 @@ class BrokerConnection(object):
140
148
'node_id' : 0 ,
141
149
'request_timeout_ms' : 40000 ,
142
150
'reconnect_backoff_ms' : 50 ,
151
+ 'reconnect_backoff_max_ms' : 1000 ,
143
152
'max_in_flight_requests_per_connection' : 5 ,
144
153
'receive_buffer_bytes' : None ,
145
154
'send_buffer_bytes' : None ,
@@ -196,6 +205,7 @@ def __init__(self, host, port, afi, **configs):
196
205
assert self .config ['sasl_plain_password' ] is not None , 'sasl_plain_password required for PLAIN sasl'
197
206
198
207
self .state = ConnectionStates .DISCONNECTED
208
+ self ._reset_reconnect_backoff ()
199
209
self ._sock = None
200
210
self ._ssl_context = None
201
211
if self .config ['ssl_context' ] is not None :
@@ -301,6 +311,7 @@ def connect(self):
301
311
else :
302
312
log .debug ('%s: Connection complete.' , self )
303
313
self .state = ConnectionStates .CONNECTED
314
+ self ._reset_reconnect_backoff ()
304
315
self .config ['state_change_callback' ](self )
305
316
306
317
# Connection failed
@@ -336,6 +347,7 @@ def connect(self):
336
347
log .info ('%s: Authenticated as %s' , self , self .config ['sasl_plain_username' ])
337
348
log .debug ('%s: Connection complete.' , self )
338
349
self .state = ConnectionStates .CONNECTED
350
+ self ._reset_reconnect_backoff ()
339
351
self .config ['state_change_callback' ](self )
340
352
341
353
return self .state
@@ -471,11 +483,19 @@ def blacked_out(self):
471
483
re-establish a connection yet
472
484
"""
473
485
if self .state is ConnectionStates .DISCONNECTED :
474
- backoff = self .config ['reconnect_backoff_ms' ] / 1000.0
475
- if time .time () < self .last_attempt + backoff :
486
+ if time .time () < self .last_attempt + self ._reconnect_backoff :
476
487
return True
477
488
return False
478
489
490
+ def connection_delay (self ):
491
+ time_waited_ms = time .time () - (self .last_attempt or 0 )
492
+ if self .state is ConnectionStates .DISCONNECTED :
493
+ return max (self ._reconnect_backoff - time_waited_ms , 0 )
494
+ elif self .connecting ():
495
+ return 0
496
+ else :
497
+ return 999999999
498
+
479
499
def connected (self ):
480
500
"""Return True iff socket is connected."""
481
501
return self .state is ConnectionStates .CONNECTED
@@ -491,6 +511,19 @@ def disconnected(self):
491
511
"""Return True iff socket is closed"""
492
512
return self .state is ConnectionStates .DISCONNECTED
493
513
514
+ def _reset_reconnect_backoff (self ):
515
+ self ._failures = 0
516
+ self ._reconnect_backoff = self .config ['reconnect_backoff_ms' ] / 1000.0
517
+
518
+ def _update_reconnect_backoff (self ):
519
+ if self .config ['reconnect_backoff_max_ms' ] > self .config ['reconnect_backoff_ms' ]:
520
+ self ._failures += 1
521
+ self ._reconnect_backoff = self .config ['reconnect_backoff_ms' ] * 2 ** (self ._failures - 1 )
522
+ self ._reconnect_backoff = min (self ._reconnect_backoff , self .config ['reconnect_backoff_max_ms' ])
523
+ self ._reconnect_backoff *= uniform (0.8 , 1.2 )
524
+ self ._reconnect_backoff /= 1000.0
525
+ log .debug ('%s: reconnect backoff %s after %s failures' , self , self ._reconnect_backoff , self ._failures )
526
+
494
527
def close (self , error = None ):
495
528
"""Close socket and fail all in-flight-requests.
496
529
@@ -508,6 +541,7 @@ def close(self, error=None):
508
541
log .info ('%s: Closing connection. %s' , self , error or '' )
509
542
self .state = ConnectionStates .DISCONNECTING
510
543
self .config ['state_change_callback' ](self )
544
+ self ._update_reconnect_backoff ()
511
545
if self ._sock :
512
546
self ._sock .close ()
513
547
self ._sock = None
0 commit comments