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 才发现.
| SessionTimeout | KeepAlive 推荐 |
|---|---|
| 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 / 带宽开销, 完全可忽略.