@@ -25,6 +25,7 @@ use rabbitmq_stream_protocol::{
25
25
delete:: Delete ,
26
26
delete_publisher:: DeletePublisherCommand ,
27
27
generic:: GenericResponse ,
28
+ heart_beat:: HeartBeatCommand ,
28
29
metadata:: MetadataCommand ,
29
30
open:: { OpenCommand , OpenResponse } ,
30
31
peer_properties:: { PeerPropertiesCommand , PeerPropertiesResponse } ,
@@ -54,10 +55,11 @@ use self::{
54
55
use std:: {
55
56
collections:: HashMap ,
56
57
sync:: { atomic:: AtomicU64 , Arc } ,
58
+ time:: { Duration , Instant } ,
57
59
} ;
58
60
use std:: { future:: Future , sync:: atomic:: Ordering } ;
59
- use tokio:: sync:: RwLock ;
60
61
use tokio:: { net:: TcpStream , sync:: Notify } ;
62
+ use tokio:: { sync:: RwLock , task:: JoinHandle } ;
61
63
use tokio_util:: codec:: Framed ;
62
64
63
65
type SinkConnection = SplitSink < Framed < TcpStream , RabbitMqStreamCodec > , Request > ;
@@ -69,6 +71,8 @@ pub struct ClientState {
69
71
handler : Option < Arc < dyn MessageHandler > > ,
70
72
heartbeat : u32 ,
71
73
max_frame_size : u32 ,
74
+ last_heatbeat : Instant ,
75
+ heartbeat_task : Option < JoinHandle < ( ) > > ,
72
76
}
73
77
74
78
#[ async_trait:: async_trait]
@@ -77,6 +81,7 @@ impl MessageHandler for Client {
77
81
match & item {
78
82
Some ( Ok ( response) ) => match response. kind_ref ( ) {
79
83
ResponseKind :: Tunes ( tune) => self . handle_tune_command ( tune) . await ,
84
+ ResponseKind :: Heartbeat ( _) => self . handle_heart_beat_command ( ) . await ,
80
85
_ => {
81
86
if let Some ( handler) = self . state . read ( ) . await . handler . as_ref ( ) {
82
87
let handler = handler. clone ( ) ;
@@ -132,6 +137,8 @@ impl Client {
132
137
handler : None ,
133
138
heartbeat : broker. heartbeat ,
134
139
max_frame_size : broker. max_frame_size ,
140
+ last_heatbeat : Instant :: now ( ) ,
141
+ heartbeat_task : None ,
135
142
} ;
136
143
let mut client = Client {
137
144
dispatcher,
@@ -172,6 +179,14 @@ impl Client {
172
179
CloseRequest :: new ( correlation_id, ResponseCode :: Ok , "Ok" . to_owned ( ) )
173
180
} )
174
181
. await ?;
182
+
183
+ let mut state = self . state . write ( ) . await ;
184
+
185
+ if let Some ( heartbeat_task) = state. heartbeat_task . take ( ) {
186
+ heartbeat_task. abort ( ) ;
187
+ }
188
+
189
+ drop ( state) ;
175
190
self . channel . close ( ) . await
176
191
}
177
192
pub async fn subscribe (
@@ -378,10 +393,10 @@ impl Client {
378
393
Ok ( ( ) )
379
394
}
380
395
381
- fn max_value ( & self , client : u32 , server : u32 ) -> u32 {
396
+ fn negotiate_value ( & self , client : u32 , server : u32 ) -> u32 {
382
397
match ( client, server) {
383
398
( client, server) if client == 0 || server == 0 => client. max ( server) ,
384
- ( client, server) => client. max ( server) ,
399
+ ( client, server) => client. min ( server) ,
385
400
}
386
401
}
387
402
@@ -470,11 +485,35 @@ impl Client {
470
485
471
486
async fn handle_tune_command ( & self , tunes : & TunesCommand ) {
472
487
let mut state = self . state . write ( ) . await ;
473
- state. heartbeat = self . max_value ( self . opts . heartbeat , tunes. heartbeat ) ;
474
- state. max_frame_size = self . max_value ( self . opts . max_frame_size , tunes. max_frame_size ) ;
488
+ state. heartbeat = self . negotiate_value ( self . opts . heartbeat , tunes. heartbeat ) ;
489
+ state. max_frame_size = self . negotiate_value ( self . opts . max_frame_size , tunes. max_frame_size ) ;
475
490
476
491
let heart_beat = state. heartbeat ;
477
492
let max_frame_size = state. max_frame_size ;
493
+
494
+ trace ! (
495
+ "Handling tune with frame size {} and heartbeat {}" ,
496
+ max_frame_size,
497
+ heart_beat
498
+ ) ;
499
+
500
+ if let Some ( task) = state. heartbeat_task . take ( ) {
501
+ task. abort ( ) ;
502
+ }
503
+
504
+ if heart_beat != 0 {
505
+ let heartbeat_interval = ( heart_beat / 2 ) . max ( 1 ) ;
506
+ let channel = self . channel . clone ( ) ;
507
+ let heartbeat_task = tokio:: spawn ( async move {
508
+ loop {
509
+ trace ! ( "Sending heartbeat" ) ;
510
+ let _ = channel. send ( HeartBeatCommand :: default ( ) . into ( ) ) . await ;
511
+ tokio:: time:: sleep ( Duration :: from_secs ( heartbeat_interval. into ( ) ) ) . await ;
512
+ }
513
+ } ) ;
514
+ state. heartbeat_task = Some ( heartbeat_task) ;
515
+ }
516
+
478
517
drop ( state) ;
479
518
480
519
let _ = self
@@ -484,4 +523,10 @@ impl Client {
484
523
485
524
self . tune_notifier . notify_one ( ) ;
486
525
}
526
+
527
+ async fn handle_heart_beat_command ( & self ) {
528
+ trace ! ( "Received heartbeat" ) ;
529
+ let mut state = self . state . write ( ) . await ;
530
+ state. last_heatbeat = Instant :: now ( ) ;
531
+ }
487
532
}
0 commit comments