KeepAlive 心跳
OpcUaSession 内置 KeepAlive 心跳: 周期性读 ServerStatus.CurrentTime (i=2258) 做探测, 既维持会话不超时, 又能在第一时间发现连接异常.
keep_alive_interval_ms
@property
def keep_alive_interval_ms(self) -> int: ...
@keep_alive_interval_ms.setter
def keep_alive_interval_ms(self, value: int) -> None: ...
| 项 | 值 |
|---|---|
| 单位 | 毫秒 |
| 默认 | 10000 (10 秒) |
| 最小推荐 | 1000 |
| 0 | 禁用 |
修改后立即生效:
ua.keep_alive_interval_ms = 5000 # 改成 5 秒
构造时传:
OpcUaSession(..., keepalive_interval_ms=5000)
自动启停
| 时机 | 行为 |
|---|---|
connect() 成功后 | 自动启动 |
disconnect() | 自动停止 |
修改 keep_alive_interval_ms (已连接时) | 重启 |
| 重连成功 | 自动启动 |
KeepAlive 跑在独立 Python 线程 (threading.Thread(daemon=True)), 名 DarraOpcUa-KeepAlive.
心跳事件
每次心跳成功都触发 events.keep_alive:
ua.events.keep_alive.append(
lambda e: print(f"KeepAlive @ {e.server_time_utc.isoformat()} (status={e.server_status.name})"))
心跳失败 (read 抛异常) 触发 events.communication_error:
def on_err(e):
if e.category == "keepalive":
print(f"Heartbeat lost: {e.status_code.name} {e.message}")
ua.events.communication_error.append(on_err)
与 SessionTimeout 的关系
服务端 session_timeout_ms 默认 10 分钟. 客户端的 KeepAlive 必须比 SessionTimeout 短得多 (推荐 ~1/10), 否则服务端会因为 "Session 长时间无活动" 主动 Cleanup, 客户端拿到 BadSessionIdInvalid 才发现.
| SessionTimeout | KeepAlive 推荐 |
|---|---|
| 10 分钟 (默认) | 10 秒 (默认) |
| 5 分钟 | 5 秒 |
| 1 小时 | 30 秒 |
为什么用 ServerStatus.CurrentTime
OPC UA 没有专门的 KeepAlive 协议消息, 通常的做法是发一个轻量级 read 请求做心跳. ServerStatus.CurrentTime (i=2258) 是 OPC Foundation 标准节点, 任何符合规范的 Server 都有, read 开销极小.
自定义心跳逻辑
如果想完全禁用 SDK 内置心跳, 自己实现:
import threading, time
from darra_opcua import OpcUaException
ua.keep_alive_interval_ms = 0 # 禁用 SDK 内置
stop = threading.Event()
def hb():
while not stop.wait(5.0):
if not ua.is_connected:
continue
try:
with ua.read("i=2258") as dv: # 或读"业务心跳节点"
pass
except OpcUaException as ex:
print(f"heartbeat: {ex}")
threading.Thread(target=hb, daemon=True).start()
性能影响
KeepAlive 每次开销:
- 1 次 read RPC (网络往返 + 服务端读 + 序列化)
- 典型局域网 ~3-5 ms
- 远程网络 ~50-200 ms
10 秒周期 = ~0.5% CPU / 带宽开销, 完全可忽略.