futu_backend/
heartbeat.rs1use std::sync::Arc;
13use std::time::Duration;
14
15use crate::conn::BackendConn;
16use crate::proto_internal::ft_conn_heart_beat::HeartBeatReq;
17
18pub const CMD_HEARTBEAT_PLATFORM: u16 = 6003;
20pub const CMD_HEARTBEAT_BROKER: u16 = 1003;
22
23pub fn start_heartbeat(conn: Arc<BackendConn>, interval: Duration) -> tokio::task::JoinHandle<()> {
25 start_heartbeat_with_cmd(conn, interval, CMD_HEARTBEAT_PLATFORM, "platform")
26}
27
28pub fn start_broker_heartbeat(
30 conn: Arc<BackendConn>,
31 interval: Duration,
32) -> tokio::task::JoinHandle<()> {
33 start_heartbeat_with_cmd(conn, interval, CMD_HEARTBEAT_BROKER, "broker")
34}
35
36fn start_heartbeat_with_cmd(
38 conn: Arc<BackendConn>,
39 interval: Duration,
40 cmd_id: u16,
41 channel_name: &'static str,
42) -> tokio::task::JoinHandle<()> {
43 tokio::spawn(async move {
44 let mut ticker = tokio::time::interval(interval);
45 ticker.tick().await; let mut fail_count = 0u32;
47 const MAX_FAILURES: u32 = 3;
48
49 loop {
50 ticker.tick().await;
51 if !conn.is_connected() {
52 tracing::error!(
53 channel = channel_name,
54 "heartbeat observed disconnected backend conn; exiting to trigger reconnect"
55 );
56 break;
57 }
58
59 let req = HeartBeatReq {
60 pre_time_delay: Some(0),
61 };
62 let body = prost::Message::encode_to_vec(&req);
63
64 match conn.request(cmd_id, body).await {
65 Ok(resp) => {
66 tracing::trace!(
67 channel = channel_name,
68 cmd_id,
69 body_len = resp.body.len(),
70 "backend heartbeat ok"
71 );
72 fail_count = 0; }
74 Err(e) => {
75 fail_count += 1;
76 tracing::warn!(
77 channel = channel_name,
78 cmd_id,
79 error = %e,
80 fail_count,
81 max = MAX_FAILURES,
82 "backend heartbeat failed"
83 );
84 if fail_count >= MAX_FAILURES {
85 tracing::error!(
86 channel = channel_name,
87 "heartbeat failed {MAX_FAILURES} times, connection likely lost"
88 );
89 break;
90 }
91 }
92 }
93 }
94 })
95}