设备在线状态
DeviceChain 为每台设备维护实时在线状态信号:当前是否在线,以及最近连接、断开或报告活动的时间。在线状态属于设备的最新已知状态,与最新测量保存在同一投影中。控制台设备的**连接状态(Connectivity)**选项卡显示这些信息。
DeviceChain 判断在线的方式取决于传输,因此本页从这里开始。
两种确定在线状态的方式
每台设备携带在线状态来源,说明如何确定在线/离线状态。
推断(Inferred)是默认方式。传输没有提供显式连接或断开信号,因此根据活动推断。发送数据期间,设备视为在线。如果静默超过不活跃超时,后台扫描将其标记为离线。这适合普通 HTTP、CoAP 等无连接传输。
**明确上报(Asserted)**表示传输明确告知 DeviceChain 设备连接和断开,因此状态是确定信息,而非猜测。设备首次收到这种信号时,DeviceChain 将它切换到明确上报来源。此后:
- 在线/离线状态只由显式连接/断开信号驱动。平台已获知设备离线后,迟来的数据包绝不能把它标记回在线。
- 不活跃扫描不再处理它。明确上报设备静默时不推定失联,因为负责明确报告离线的传输中,静默不是离线证据。混用两者会让长间隔上报设备在已知连接时仍被标为离线。
直到提供明确状态的传输产生信号,设备都保持推断方式。现有设备不受影响,除非开始使用明确上报状态的传输。
明确上报在线状态的传输
目前三种传输明确上报在线状态:
- 普通 MQTT,适用于连接 DeviceChain 自身消息代理的设备。代理已经知道连接打开和关闭的时刻,DeviceChain 直接读取。参见平台消息代理上的 MQTT。
- Sparkplug-B,其 BIRTH 和 DEATH 消息正是显式连接/断开信号。
- LwM2M,注册生命周期提供相同信息:注册、定期更新、注销或有效期到期。
明确上报设备没有不活跃兜底
跳过不活跃扫描是有意的,必须考虑其后果:明确上报设备没有不活跃兜底。 离线信号只能来自传输。如果信号始终未到达,设备会一直显示在线,没有其他机制纠正。例如:
- Sparkplug 离线消息随连接一起丢失。
- LwM2M 设备的注册有效期尚未结束。LwM2M 自身默认有效期是 86400 秒,即整整一天。
运行边缘服务介绍了观察重点和如何限制窗口。如果本应报告断开的传输已永久消失,而不仅是静默,请将设备改回推断在线状态,使状态可以再次纠正。
平台消息代理上的 MQTT
MQTT 在线状态无需设备配合。设备不必发布上线消息、设置遗嘱,或以任何方式声明自己。
需要配齐的是实例,而非设备。event-sources 中读取消息代理连接的消息代理监听器(broker tap),下文简称“监听器”,需要两项条件:
- NATS 系统账户凭据;
- 指向本实例自身消息代理的事件来源。
同时具备前,监听器保持关闭,MQTT 设备使用推断方式。dcctl bootstrap 生成凭据并配置来源,因此这样初始化的实例无需额外操作就能明确上报 MQTT 在线状态。单独执行 helm install 则留空凭据,不提供该能力。确认监听器运行帮助你判断当前情况。
曾经有监听器但之后丢失凭据的实例,与从未有过监听器不同。原先明确上报的设备否则会冻结在最后状态,因此实例将它们改回推断方式。参见将设备改回推断在线状态。
使用 MQTT 在线状态前,还有两点需要了解:
- 跟随设备主连接。 设备可以给客户端 ID 添加后缀,打开额外连接,例如双终端
mosquitto_sub/mosquitto_pub工作流。在线状态有意忽略这些额外连接,只跟踪主会话,因此关闭旁路连接不会把仍连接设备标为离线。如果设备始终只使用带后缀客户端 ID,则完全不明确上报状态,仍采用推断。 - 只覆盖本实例消息代理上的设备。 DeviceChain 无法观察你自行运行的消息代理连接,经外部代理到达 DeviceChain 的设备保持推断方式。
修复遗漏的 MQTT 断开
对于 MQTT 设备,DeviceChain 自行补上无兜底缺口。消息代理有一种情况无法报告设备消失:代理重启时,原有连接消失,却不会为它们发布断开通知。
因此 DeviceChain 定期将消息代理实际连接列表与平台认知比较,双向纠正差异:
- 平台原本不知道已连接的设备;
- 平台认为已连接、但消息代理没有连接的设备。
消息代理重启后重连的设备,通过自身重连纠正;其他设备由之后能覆盖整个集群的比较纠正,详见下文及缩容注意事项。
除非能确认消息代理集群每个节点,比较不会将任何设备标为离线。一个节点缓慢或不可达时,其设备从列表缺失,与真正消失的设备无法区分。错误地把在线设备标为离线更具破坏性,因为所有依赖在线状态的功能都会响应:连接状态选项卡显示离线,连接状态规则为始终可达的设备触发断开告警。此时 DeviceChain 仍将新发现设备标为在线,等待下一轮再判断离线。
在线状态来源显示在哪里
凡是来源会影响读数含义的地方,都会显示:
- 控制台。 连接状态选项卡显示来源:由传输上报或根据活动推断。它还区分传输明确报告的已断开(Disconnected),与仅仅离线(Offline)。离线只说明近期没有数据,健康但上报间隔较长的设备也可能如此。
- MCP。
get_device_state在状态旁返回presenceSource,并提醒助手不要将推断为不活跃的设备报告为故障。 - API。
presenceSource是device-state的DeviceState类型字段,返回ASSERTED或INFERRED。
为什么这种区别重要
推断方便,但延迟且不明确。“离线”只意味着“近期没有通信”,实际断开发现缓慢,对长间隔上报设备也不准确。明确上报即时且明确:传输报告的瞬间就确定断开。这适合需要告警或执行动作的场景。
模式是明确的逐设备标志,因此无连接设备保留熟悉的超时行为,能够报告状态的传输设备获得确定信号,两者绝不干扰。
推断与明确上报两种设备在线状态都已提供。三种传输明确上报:DeviceChain 自身消息代理的普通 MQTT(实例必须先具备上述系统账户凭据)、Sparkplug-B 和 LwM2M。检测规则可在连接/断开边沿触发,参见在线状态规则。
在线状态规则
检测规则可以直接在连接/断开边沿触发。连接状态条件在明确断开到达时立即触发告警,重连时解除,无需调整超时。
- 规则表单。 控制台规则表单提供**连接状态(Connectivity)**类型。没有需要编写的条件,因为在线状态边沿就是信号。已有连接状态规则按自身类型打开。如果存储定义携带表单无法表示的内容,会在保存前说明,而不是静默替换。
- 自动化画布。 画布提供**连接状态(Connectivity)**节点,无需配置,只需连接来源和动作。与表单一样,画布不会静默重写无法完整显示的已存规则,而是说明并禁用保存。
连接状态条件补充基于超时的缺失规则,分别对应明确离线和推断静默,设计上应配合使用。
明确断开也更新设备实时状态,因此传输一报告,连接状态选项卡立即显示离线。
在线状态运维
在线状态是否可靠,取决于其背后的信号。两种明确上报状态的边缘传输都由单个拥有所有权的副本运行,因此在基于在线状态告警前,应了解切换代价、设备为何可能一直在线,以及如何限制这种情况。参见运行边缘服务。
以下介绍消息代理明确上报 MQTT 在线状态的专属特性。
确认消息代理监听器运行
从 DeviceChain 自身消息代理读取连接需要四项条件。缺少任一项,监听器都会拒绝启动,记录原因并设置 presence_tap_off{reason} 说明具体哪项。从未明确上报的 MQTT 设备保持推断,看起来与从未提供该能力的实例相同,因为功能上确实如此。四项要求是:
brokerPresence.enabled未设为false。- 配置了 NATS 系统账户凭据。
dcctl bootstrap会生成;手动组装实例缺少监听器,通常因为没有凭据。 - 至少一个事件来源指向平台自身消息代理。否则没有连接通知可读。
- 配置了服务间调用。否则监听器没有修复路径,因此有意关闭,而不是部分可用:消息代理未报告断开的设备会永远显示已连接。
运行中的监听器关闭时
从未运行监听器的实例,到此已说明全部情况。曾经运行过的实例还有第二个问题:已标记明确上报的设备保持最后状态,因为它们不受不活跃扫描影响,数据事件也不能改变其状态。
对于前两种原因,即显式 enabled: false 和缺少系统账户凭据,event-sources 自行将这些设备改回推断方式。它们是每个副本一致读取的配置,因此基于这些条件自动释放整个设备群是安全的。
监听器无法访问消息代理时也会释放,理由比配置更强:设备所连接的 MQTT 网关就在同一消息代理中,因此代理不可达期间,也没有设备通过它连接。监听器等待三十秒让连接建立后才决定。因此触发条件是半分钟没有系统账户连接,不是一次失败,足以避免将与服务同时重启的代理误认为永久消失。
监听器启动后永久丢失连接会重启 event-sources。 消息代理永久关闭已运行监听器连接时,例如不再接受系统账户凭据,event-sources 存活检查失败,Kubernetes 重启 Pod。凭据拒绝同时影响每个副本,因此所有 event-sources Pod 都会重启。期间 HTTP 接入不可用;MQTT 遥测仍由代理存储,待服务返回后处理。如果重启后仍无法登录,监听器以 broker_unreachable 原因关闭,按此处说明释放它曾明确上报的设备,并继续检查。因此,该原因也包括消息代理运行中但拒绝凭据的情况。
仅对于这一原因,释放前的两分钟等待(参见将设备改回推断在线状态)实际上是重新检查,而不是单纯延迟;这能防止释放持续到引发它的故障结束后。每轮开始前,包括第一轮,服务重新连接系统账户。消息代理响应时,不释放任何设备:服务退出,Pod 重启,监听器正常启动。因此,消息代理恢复会导致 Pod 重启,而不是整群设备被改为推断。另两种释放路径无法这样工作,也无需如此,因为都是启动时读取一次的配置。显式 enabled: false 不等待两分钟,约三十秒内开始释放,随机延迟避免副本同时开始。凭据缺失时,两分钟等待的是配置变更滚动更新产生的替代 Pod。
其余三种情况不会自动释放:
- 没有指向平台消息代理的来源;
- 没有服务间配置;
- 已经成功到达消息代理的连接上,订阅失败。
这些情况下使用 dcctl presence demote。自动释放和手动命令都见将设备改回推断在线状态。
监听器未运行的信号
两个信号说明监听器未运行,覆盖不同故障。
presence_tap_off{reason} 是直接信号。每条拒绝启动路径都会将它设为 1,标签指出原因。它回答设备群静默时原本无法回答的问题:长期 MQTT 设备群可以数天没有连接/断开通知,因此普通在线状态事件流无法区分正在监听的实例与从未成功启动监听器的实例。
它不覆盖启动后停止工作的监听器,因为没有拒绝启动发生。presence_canary_missed_total 覆盖这种情况,是应设置告警的计数器。 服务每分钟打开自己的 MQTT 连接,只为了让正常监听器能够观察。健康监听器的 presence_canary_observed_total 增长,链路损坏时 presence_canary_missed_total 增长。
探针按自身计划运行,独立于下述修复扫描。这种分离使计数器可信:只有被观察对象健康时才能报告的测量工具,会在最重要时刻沉默。
其他在线状态计数器应理解为流量,而非健康。安静设备群的 presence_events_total 可以正常保持不变;刚关闭的监听器中它也可以正常变化,因为释放设备会为每台设备产生 presence_events_total{state="demoted"} 事件。两种趋势都不能说明是否仍在读取在线状态。
调整监听器
监听器带有可用默认值,大多数实例无需修改。设置位于 event-sources 区域的 brokerPresence 配置中。
| 设置 | 默认值 | 作用 |
|---|---|---|
enabled | 未设置时开启 | 运行监听器。设为 false 有意关闭消息代理明确上报 MQTT 状态,例如代理与不允许系统账户订阅者的系统共享。MQTT 设备随后使用推断,监听器原先明确上报的设备在之后几分钟内按节奏改回推断。 |
reconcileSeconds | 300 | 消息代理实际连接列表与平台列表双向比较的频率。这不是兜底。 正常重启不报告任何断开,因此这轮扫描是唯一纠正这些设备的机制,明确上报设备也没有不活跃扫描。降低值可加快修复,代价是每轮进行一次全代理集群清点和每租户一次读取。 |
canarySeconds | 60 | 服务打开自身 MQTT 连接证明监听器存活的频率,也是 presence_canary_missed_total 计数对应的计划。 |
canaryDeadlineSeconds | 15 | 单次探针时限。过短会报告不存在的故障。 |
inventoryGatherSeconds | 5 | 扫描收集代理集群回复的时间。过短会将缓慢节点视为缺失,使该轮所有断开判断被暂缓。 |
四项时间间隔设为非正值时回退到上述默认值,而不是零。
修复扫描超时会明确报告
每轮修复遍历实例所有租户,并读取其明确上报设备,大实例扫描时间较长。扫描有预算限制,无法及时完成就停止,报告 presence_reconcile_runs_total{outcome="timeout"},并记录处理了多少租户。
下轮从首个未处理租户继续,而不是从头开始。否则,预算始终不够的设备群每次只修复前几个租户,其余永远不会处理,不是延迟,而是永远没有。
- 偶尔
timeout表示修复落后,但每个租户仍轮得到。 - 持续出现表示实例需要更多余量。轮转末尾设备遗漏断开的纠正最晚。
presence_reconcile_runs_total 为每轮提供一个结果:
| 结果 | 含义 |
|---|---|
complete | 已在完全确认的消息代理集群上遍历所有租户 |
partial | 已运行,但不是所有消息代理节点都回答,设备只会标为在线,绝不离线 |
timeout | 预算耗尽,未处理租户下轮优先 |
failed | 无法读取任何内容:没有消息代理清单、没有租户列表,或没有任何租户的在线状态。协调完全没有执行 |
cancelled | 服务在扫描中关闭,不是故障 |
请与探针一起为 failed 设置告警。单个租户状态读取失败可容忍,其他租户仍处理。全部读取失败意味着修复完全停止,device-state 故障在这里就是这种表现。
状态变化计入租户接入上限
连接与断开变化通过与遥测相同的每租户接入限制。租户达到上限时会被拒绝,计入 presence_events_refused_total。
这是有意的。连接反复变化完全由设备控制,否则没有成本:不断重连的设备会成为接入限制看不到的无计量写入放大器。
必须考虑其后果。达到上限的租户可能有错误在线/离线状态,直到后续协调修复(默认最长五分钟)。这段时间所有依赖在线状态的功能也错误,包括连接状态规则和离线设备保留命令的释放。
降级到推断方式也经过相同检查。 达到上限的租户,其修复可能与造成压力的连接抖动一起被拒绝。不会丢失,因为被拒绝释放会让设备保持明确上报,下轮再次找到,但修复无法早于上限允许的时间。
这适用于平台消息代理 MQTT 监听器。Sparkplug 对 DATA 读数计入租户上限,但其上线/离线明确上报变化不计入。LwM2M 使用自己的独立配置限制。
背压不同。 平台拒绝新事件期间(参见接入路径背压),所有传输的连接/断开变化仍被接受,因为拒绝会让设备状态错误直到协调修复。背压检查不限制这些变化的数量(上述租户上限仍适用于消息代理监听器),因此不断重连的设备群仍可能填满消息流容量,之后消息流丢弃最旧事件。
调整消息代理集群规模需要重启 event-sources
修复比较只有确认全部代理节点才标记离线。它按曾见过的最大集群确定“全部节点”。这个数量只增不减,防止网络分区造成大量错误断开:路由隔离的代理会报告自己是整个集群,否则就能满足自己的检查。
代价是无法区分一种情况:有意缩小消息代理集群。回复节点数少于记忆最大值,之后每轮都被视为不完整,离线修复永远不执行,不只是下一轮之前,而是整个进程生命周期。被移除节点遗留的设备无限期在线,且明确上报设备没有不活跃兜底,超时也不会纠正。
缩小 NATS 集群后,重启 event-sources。 需要重启却未执行时,presence_reconcile_withheld_disconnects_total 持续增长。扩容不需要操作。
如果重启必须等待,遗留设备无需等待。对该来源运行 dcctl presence demote,将设备改回推断方式,十分钟不活跃扫描可根据自身证据将其标为离线。参见将设备改回推断在线状态。降级修复已错误的设备,重启仍是防止后续设备出错的必要步骤。