跳到主要内容

KeepAlive 心跳

Session 内置 KeepAlive 心跳: 周期性读 ServerStatus.CurrentTime (i=2258) 做探测, 既维持会话不超时, 又能在第一时间发现连接异常.

前置阅读

API

pub fn keep_alive_interval_ms(&self) -> u32;
pub fn set_keep_alive_interval_ms(&mut self, ms: u32);
单位毫秒
默认10000 (10 秒) (在 ConnectionConfig::keepalive_interval_ms 改)
最小推荐1000
0禁用

修改后立即生效:

s.set_keep_alive_interval_ms(5000);   // 改成 5 秒

自动启停

时机行为
connect() 成功后自动启动
disconnect()自动停止
set_keep_alive_interval_ms(...)重启
重连成功自动启动

KeepAlive 通过独立 std::thread 实现 (无 tokio 依赖). 内部用 Arc<AtomicBool> stop flag 做线程取消, Drop Session 时一并 stop.store(true) + join().


心跳事件

每次心跳成功都触发 on_keep_alive:

s.events.lock().unwrap().on_keep_alive.push(Box::new(|e| {
println!("KeepAlive @ {:?} (server status={})",
e.server_time_utc, e.server_status);
}));

心跳失败 (Read CurrentTime 失败) 触发 on_communication_error:

s.events.lock().unwrap().on_communication_error.push(Box::new(|e| {
if e.category == "keepalive" {
eprintln!("Heartbeat lost: {} {}", e.status_code, e.message);
}
}));

KeepAliveEventArgs:

pub struct KeepAliveEventArgs {
pub server_time_utc: SystemTime,
pub server_status: StatusCode,
pub timestamp_utc: SystemTime,
}

与 SessionTimeout 的关系

服务端 session_timeout_ms 默认 10 分钟. 客户端的 KeepAlive 必须比 SessionTimeout 短得多 (推荐 ~1/10), 否则服务端会因为 "Session 长时间无活动" 主动 Cleanup, 客户端拿到 BAD_SESSION_ID_INVALID 才发现.

SessionTimeoutKeepAlive 推荐
10 分钟 (默认)10 秒 (默认)
5 分钟5 秒
1 小时30 秒

为什么用 ServerStatus.CurrentTime

OPC UA 没有专门的 KeepAlive 协议消息, 通常的做法是发一个轻量级 Read 请求做心跳. ServerStatus.CurrentTime (i=2258) 是 OPC Foundation 标准节点, 任何符合规范的 Server 都有, Read 开销极小.


自定义心跳逻辑

如果想完全禁用 SDK 内置心跳, 自己起线程:

use std::time::Duration;
use std::sync::Arc;
use std::sync::atomic::{AtomicBool, Ordering};

s.set_keep_alive_interval_ms(0); // 禁用 SDK 内置

let stop = Arc::new(AtomicBool::new(false));
let stop2 = stop.clone();
// 把 Session 包成 Arc 跨线程共享 (Session 实现 Send + Sync)
let s_arc = Arc::new(s);
let s2 = s_arc.clone();
std::thread::spawn(move || {
while !stop2.load(Ordering::SeqCst) {
std::thread::sleep(Duration::from_secs(5));
if !s2.is_connected() { continue; }
let _ = s2.read("i=2258"); // 自己的心跳
}
});

// 退出
stop.store(true, Ordering::SeqCst);

性能影响

KeepAlive 每次开销:

  • 1 次 Read RPC (网络往返 + 服务端读 + 序列化)
  • 典型局域网 ~3-5 ms
  • 远程网络 ~50-200 ms

10 秒周期 = ~0.5% CPU / 带宽开销, 完全可忽略.

下一步