ELI5 · 说人话
影子不是「机器人的缓存」。它是一张双方各写一半的契约:业务只写「我要它变成什么样」,设备只写「我现在是什么样」,中间那个差,系统自己算。
最核心的一件事
delta 是算出来的,不是写出来的。谁都不能直接修改它。它为空的那一刻,就是这台设备收敛完成的那一刻——你连「状态到位了吗」都不用另外设计一套判断。
一份影子长什么样
{
"deviceId": "rb-8842",
"version" : 1487, ← 每次变更 +1
"state": {
"desired" : { "mode":"cleaning", "floor":3 },
"reported": { "mode":"idle", "floor":1, "battery":62 },
"delta" : { "mode":"cleaning", "floor":3 } ← 系统算的
},
"metadata": { ← 逐字段时间戳
"reported": { "mode":{"ts":1735900012}, "battery":{"ts":1735900041} }
},
"connection": { "online":true, "lastSeen":1735900041 }
}
只存 reported 就退化成缓存,只存 desired 就退化成指令队列。三块同时在,才有「差」可算。
乐观锁、乱序去重、心跳对账,全靠它。别拿时间戳当版本——两台机器的时钟不一样。
最容易被省掉、也最容易出事的一块。下一节专门讲它防的是什么。
「离线」是平台判定的,「idle」是设备报的。混进一个字段,你永远说不清它是断了还是闲着。
最容易省掉的那一块
这不是理论问题。MQTT QoS 1 的重传、网关分区切换、设备补发离线积压——每一个都会制造乱序。整份文档一个时间戳时你只能整包取舍;逐字段时间戳让你只丢那一个字段,同一个包里更新的其他字段照常生效。
别什么都往里塞
清洁模式、目标楼层、最大速度、灯光开关。
业务写 desired,设备写 reported,两边都有,会产生 delta。
电量、坐标、温度、故障码。
只有 reported,永远不许出现在 desired 里——你没法「期望」电量是 80%。
型号、序列号、固件版本、出厂参数。
上线时报一次就够,别放进热路径,单独存关系库。
一次完整的收敛
好处在断线时才显出来:设备离线时业务照样能写 desired,delta 就挂在那儿。设备一重连,delta 自动推下去,不需要业务重发、不需要补偿任务、也不用维护一张「待下发指令表」。
最该想清楚的一个判断
同一个平台两条路都要有,别指望一条通吃。硬套的下场:
{"action":"restart"} 挂在 desired 里,设备每次重连拉到 delta 就重启一次。永远收敛不了,因为「重启」不是一个状态。
「设成清洁模式」发成指令,设备当时离线就丢了。于是你得再造一张待下发表、一个重试器、一套过期清理——把影子重新发明一遍。
一个数字,三个用途
业务写 desired 时带上刚读到的 version。对不上就拒绝,让它重读再写。两个大屏同时改同一台车,不会互相盖掉。
设备收到 version 比本地还小的 delta,直接丢。重传、补发、多网关并发下推,全被这一行判断挡住。
心跳里捎上 version。云端一比,一样就什么都不做,不一样才拉全量。100 万台每 30 秒只多传几百 KB。
100 万份影子放哪
影子必须给 TTL 兜底。设备报废、被拆走、换了 ID,没人会来告诉你——没有过期时间,Redis 里会慢慢堆满几年前的僵尸设备。
变了怎么告诉别人
硬规矩:变更事件只带 diff,不带全量。100 万台设备的差别就在这里——
100 万台 × 2 KB ÷ 30 秒
还没算下游各方各存一份
按 5% 设备有变化、每条 200 B 算
相差两百倍
红线
影子只回答一个问题:这台设备现在长什么样。任务、工单、轨迹各有各的表。影子一旦变胖,每次推送都在浪费带宽。
给 desired 加 TTL 和写入时间。设备一上线就执行三天前的意图,轻则跑错,重则出事故。过期的要清掉并告警。
desired 里只能放状态,不能放动词。「重启」「拍照」「送快递」这类一次性动作走指令通道,带 cmdId 和 ACK。
空 delta 只说明状态字段对上了,不说明业务目标达成。「送到 302」的完成信号是一条 task.finished 事件,不是影子里的某个字段。
照常接受,但要如实回「已排队,尚未生效」。这正是影子存在的价值——把「意图」和「送达」解耦。骗业务说成功了才是真的错。
不需要。规则只有两条:观测值永远 reported 赢,控制意图永远 desired 赢。两边写的本来就是不同的字段,不该冲突。
指令说的是「去做这件事」,
影子说的是「成为这个样子」。