22
33import logging
44import threading
5- import time
65
76from amqpstorm .exception import AMQPConnectionError
87
1211class Heartbeat (object ):
1312 """AMQP Internal Heartbeat Checker"""
1413
15- def __init__ (self , interval ):
14+ def __init__ (self , interval , send_heartbeat = None ):
1615 self ._lock = threading .Lock ()
17- self ._stopped = threading .Event ()
16+ self ._running = threading .Event ()
1817 self ._timer = None
1918 self ._exceptions = None
20- self ._last_heartbeat = 0.0
21- self ._beats_since_check = 0
22- self ._interval = interval + 1
23- self ._threshold = (interval + 1 ) * 2
19+ self ._reads_since_check = 0
20+ self ._writes_since_check = 0
21+ self ._interval = interval
22+ self ._threshold = 0
23+ self .send_heartbeat = send_heartbeat
2424
25- def register_beat (self ):
25+ def register_read (self ):
2626 """Register that a frame has been received.
2727
2828 :return:
2929 """
30- with self ._lock :
31- self ._beats_since_check += 1
30+ self ._reads_since_check += 1
3231
33- def register_heartbeat (self ):
34- """Register a Heartbeat .
32+ def register_write (self ):
33+ """Register that a frame has been sent .
3534
3635 :return:
3736 """
38- with self ._lock :
39- self ._last_heartbeat = time .time ()
37+ self ._writes_since_check += 1
4038
4139 def start (self , exceptions ):
4240 """Start the Heartbeat Checker.
@@ -46,9 +44,10 @@ def start(self, exceptions):
4644 """
4745 LOGGER .debug ('Heartbeat Checker Started' )
4846 with self ._lock :
49- self ._stopped = threading .Event ()
50- self ._beats_since_check = 0
51- self ._last_heartbeat = time .time ()
47+ self ._running .set ()
48+ self ._threshold = 0
49+ self ._reads_since_check = 0
50+ self ._writes_since_check = 0
5251 self ._exceptions = exceptions
5352 self ._start_new_timer ()
5453
@@ -57,14 +56,16 @@ def stop(self):
5756
5857 :return:
5958 """
60- self ._stopped . set ()
59+ self ._running . clear ()
6160 if self ._timer :
6261 self ._timer .cancel ()
6362 self ._timer = None
6463
6564 def _check_for_life_signs (self ):
6665 """Check if we have any sign of life.
6766
67+ First check if any data has been sent, if not send a heartbeat.
68+
6869 If we have not received a heartbeat, or any data what so ever
6970 we should raise an exception so that we can close the connection.
7071
@@ -73,21 +74,28 @@ def _check_for_life_signs(self):
7374
7475 :return:
7576 """
76- if self ._stopped .is_set ():
77+ if not self ._running .is_set ():
7778 return False
7879 self ._lock .acquire ()
7980 try :
80- elapsed = time .time () - self ._last_heartbeat
81- if self ._beats_since_check == 0 and elapsed > self ._threshold :
82- self ._stopped .set ()
83- message = ('Connection dead, no heartbeat or data received '
84- 'in %ds' % round (elapsed , 3 ))
85- why = AMQPConnectionError (message )
86- if self ._exceptions is None :
87- raise why
88- self ._exceptions .append (why )
89- self ._beats_since_check = 0
81+ if self ._writes_since_check == 0 :
82+ self .send_heartbeat ()
83+ if self ._reads_since_check == 0 :
84+ self ._threshold += 1
85+ if self ._threshold >= 2 :
86+ self ._running .set ()
87+ message = ('Connection dead, no heartbeat or data received'
88+ ' in >= %ds' % (self ._interval * 2 ))
89+ why = AMQPConnectionError (message )
90+ if self ._exceptions is None :
91+ raise why
92+ self ._exceptions .append (why )
93+ return
94+ else :
95+ self ._threshold = 0
9096 finally :
97+ self ._reads_since_check = 0
98+ self ._writes_since_check = 0
9199 self ._lock .release ()
92100 self ._start_new_timer ()
93101 return True
@@ -97,7 +105,7 @@ def _start_new_timer(self):
97105
98106 :return:
99107 """
100- if self ._stopped .is_set ():
108+ if not self ._running .is_set ():
101109 return
102110 self ._timer = threading .Timer (interval = self ._interval ,
103111 function = self ._check_for_life_signs )
0 commit comments