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 ,
@@ -199,6 +208,7 @@ def __init__(self, host, port, afi, **configs):
199
208
assert self .config ['sasl_plain_password' ] is not None , 'sasl_plain_password required for PLAIN sasl'
200
209
201
210
self .state = ConnectionStates .DISCONNECTED
211
+ self ._reset_reconnect_backoff ()
202
212
self ._sock = None
203
213
self ._ssl_context = None
204
214
if self .config ['ssl_context' ] is not None :
@@ -305,6 +315,7 @@ def connect(self):
305
315
else :
306
316
log .debug ('%s: Connection complete.' , self )
307
317
self .state = ConnectionStates .CONNECTED
318
+ self ._reset_reconnect_backoff ()
308
319
self .config ['state_change_callback' ](self )
309
320
310
321
# Connection failed
@@ -340,6 +351,7 @@ def connect(self):
340
351
log .info ('%s: Authenticated as %s' , self , self .config ['sasl_plain_username' ])
341
352
log .debug ('%s: Connection complete.' , self )
342
353
self .state = ConnectionStates .CONNECTED
354
+ self ._reset_reconnect_backoff ()
343
355
self .config ['state_change_callback' ](self )
344
356
345
357
return self .state
@@ -475,11 +487,19 @@ def blacked_out(self):
475
487
re-establish a connection yet
476
488
"""
477
489
if self .state is ConnectionStates .DISCONNECTED :
478
- backoff = self .config ['reconnect_backoff_ms' ] / 1000.0
479
- if time .time () < self .last_attempt + backoff :
490
+ if time .time () < self .last_attempt + self ._reconnect_backoff :
480
491
return True
481
492
return False
482
493
494
+ def connection_delay (self ):
495
+ time_waited_ms = time .time () - (self .last_attempt or 0 )
496
+ if self .state is ConnectionStates .DISCONNECTED :
497
+ return max (self ._reconnect_backoff - time_waited_ms , 0 )
498
+ elif self .connecting ():
499
+ return 0
500
+ else :
501
+ return 999999999
502
+
483
503
def connected (self ):
484
504
"""Return True iff socket is connected."""
485
505
return self .state is ConnectionStates .CONNECTED
@@ -495,6 +515,19 @@ def disconnected(self):
495
515
"""Return True iff socket is closed"""
496
516
return self .state is ConnectionStates .DISCONNECTED
497
517
518
+ def _reset_reconnect_backoff (self ):
519
+ self ._failures = 0
520
+ self ._reconnect_backoff = self .config ['reconnect_backoff_ms' ] / 1000.0
521
+
522
+ def _update_reconnect_backoff (self ):
523
+ if self .config ['reconnect_backoff_max_ms' ] > self .config ['reconnect_backoff_ms' ]:
524
+ self ._failures += 1
525
+ self ._reconnect_backoff = self .config ['reconnect_backoff_ms' ] * 2 ** (self ._failures - 1 )
526
+ self ._reconnect_backoff = min (self ._reconnect_backoff , self .config ['reconnect_backoff_max_ms' ])
527
+ self ._reconnect_backoff *= uniform (0.8 , 1.2 )
528
+ self ._reconnect_backoff /= 1000.0
529
+ log .debug ('%s: reconnect backoff %s after %s failures' , self , self ._reconnect_backoff , self ._failures )
530
+
498
531
def close (self , error = None ):
499
532
"""Close socket and fail all in-flight-requests.
500
533
@@ -512,6 +545,7 @@ def close(self, error=None):
512
545
log .info ('%s: Closing connection. %s' , self , error or '' )
513
546
self .state = ConnectionStates .DISCONNECTING
514
547
self .config ['state_change_callback' ](self )
548
+ self ._update_reconnect_backoff ()
515
549
if self ._sock :
516
550
self ._sock .close ()
517
551
self ._sock = None
0 commit comments