对接指南
一、交互流程总览
设备影子系统涉及三方交互:设备端、应用端和平台。下图展示完整的交互闭环。
二、设备端接入
方案一:零改动接入(最小方案)
如果设备无需主动感知影子系统,不需要任何代码改动即可让影子保持最新状态:
| 步骤 | 说明 |
|---|---|
| ① | 设备照常通过标准主题上报属性:/blade/sys/{pk}/{dn}/thing/event/property/post |
| ② | 平台在 EventPropertyPostFunction 中自动旁路同步到影子的 reported(version=-1,失败不影响主流程) |
| ③ | 应用通过 REST API 设置 desired 后,设备上线时平台自动补推 Delta |
仅需额外做一件事:订阅 Delta 推送主题并处理收到的指令。
订阅主题:/blade/shadow/delta/{productKey}/{deviceName}适用场景
适用于轻量级传感器、简单控制器等不需要精确版本控制的设备。设备只需具备接收 Delta 并执行的能力。
方案二:完整接入
需要设备主动管理影子状态的场景(如启动自检、精确版本控制),按以下步骤接入。
步骤 1:订阅 Topic
设备连接 MQTT Broker 后,订阅以下主题:
| Topic | 用途 |
|---|---|
/blade/shadow/update/{pk}/{dn}_reply | 接收 reported 上报结果 |
/blade/shadow/get/{pk}/{dn}_reply | 接收影子文档查询结果 |
/blade/shadow/delta/{pk}/{dn} | 接收平台推送的 Delta |
步骤 2:上电自检——拉取影子
设备上电后,主动拉取完整影子文档,检查是否有待执行的 delta。
发布主题: /blade/shadow/get/{pk}/{dn}
{
"id": "msg-get-001",
"version": "1.0",
"method": "thing.shadow.get",
"params": {}
}平台回复(shadow/get_reply):
{
"id": "msg-get-001",
"code": 200,
"data": {
"state": {
"reported": { "temperature": 26 },
"desired": { "switch": 0 },
"delta": { "switch": 0 }
},
"metadata": { ... },
"version": 6,
"timestamp": 1711843260000
}
}设备处理逻辑:
收到影子文档后:
├── delta 非空 → 逐项执行期望值,完成后上报 reported
└── delta 为空 → 无待执行任务,进入正常运行步骤 3:上报 reported
设备将当前属性状态上报到影子。
发布主题: /blade/shadow/update/{pk}/{dn}
{
"id": "msg-update-001",
"version": "1.0",
"method": "thing.shadow.update",
"params": {
"reported": {
"temperature": 26,
"switch": 1
}
}
}| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
params.reported | Map | 是 | 要上报的属性键值对,增量合并到已有 reported |
params.version | Integer | 否 | 期望版本号,不传或 -1 跳过版本校验 |
平台回复(shadow/update_reply):
{
"id": "msg-update-001",
"code": 200,
"data": { "version": 7 }
}增量合并
reported 采用增量合并策略——仅更新本次上报的属性,不影响已有的其他属性。例如影子中已有 temperature=25,设备上报 { "switch": 1 } 后,reported 变为 { "temperature": 25, "switch": 1 }。
步骤 4:接收并处理 Delta
设备订阅 Delta 主题后,平台会在以下时机自动推送 Delta:
| 触发时机 | 说明 |
|---|---|
| 应用设置 desired 后 | 产生非空 delta 时立即推送 |
| 设备上线时 | 存在未处理的 delta 时自动补推 |
| 应用清除 desired 后 | 推送空 delta {} 通知设备撤销 |
Delta 推送报文:
{
"id": "uuid",
"version": "1.0",
"method": "thing.shadow.delta",
"params": {
"delta": { "switch": 0 },
"version": 7,
"timestamp": 1711843260000
}
}设备处理逻辑:
收到 Delta 后:
├── delta 非空 → 逐项执行,完成后上报确认
└── delta 为空 {} → 期望值已全部撤销,无需操作步骤 5:确认 Delta
设备处理完 Delta 后,上报确认和最新状态。平台收到后自动 reconcile——reported 满足 desired 时,对应的 desired 和 delta 自动清除。
发布主题: /blade/shadow/delta/{pk}/{dn}_reply
{
"id": "msg-confirm-001",
"version": "1.0",
"method": "thing.shadow.delta.reply",
"params": {
"reported": { "switch": 0 },
"version": 7
}
}注意事项
- 平台不会回复此消息(fire-and-forget 语义)
- 确认时上报的 reported 值必须与 desired 值类型和值都完全相等,否则 desired 不会被自动清除
- 常见陷阱:desired 写入字符串
"26",设备上报数字26→ 类型不一致,desired 不清除
步骤 6(可选):删除影子
删除整个影子(version=-1):
{
"id": "msg-delete-001",
"version": "1.0",
"method": "thing.shadow.delete",
"params": { "version": -1 }
}清除指定 desired 属性:
{
"id": "msg-delete-002",
"version": "1.0",
"method": "thing.shadow.delete",
"params": {
"desired": { "switch": null, "mode": null }
}
}设备端接入流程图
三、应用端接入
应用端通过 REST API 与影子系统交互,也可使用 MQTT 设置 desired。
REST API 一览
Base Path:/blade-iot/shadow
| 接口 | 方法 | 说明 |
|---|---|---|
/shadow/detail?productKey={pk}&deviceName={dn} | GET | 获取设备影子文档 |
/shadow/desired | PUT | 设置期望状态 |
/shadow/desired | DELETE | 清除期望状态 |
/shadow/remove?productKey={pk}&deviceName={dn} | DELETE | 清除整个影子 |
1. 获取设备影子
GET /blade-iot/shadow/detail?productKey={pk}&deviceName={dn}响应示例:
{
"code": 200,
"data": {
"state": {
"reported": { "temperature": 25, "switch": 1 },
"desired": { "switch": 0 },
"delta": { "switch": 0 }
},
"metadata": {
"reported": { "temperature": { "timestamp": 1711843200000 } },
"desired": { "switch": { "timestamp": 1711843260000 } }
},
"version": 5,
"timestamp": 1711843260000
}
}2. 设置期望状态
PUT /blade-iot/shadow/desired
Content-Type: application/json请求体:
{
"productKey": "pk001",
"deviceName": "device001",
"desired": {
"switch": 0,
"temperature": 26
},
"version": 5
}| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
| productKey | String | 是 | 产品标识 |
| deviceName | String | 是 | 设备名称 |
| desired | Map | 是 | 期望属性,value 为 null 表示删除该属性 |
| version | Integer | 否 | 期望版本号,不传则跳过版本校验 |
投递行为:
| 设备状态 | 平台行为 |
|---|---|
| 在线 | 立即推送 Delta → 设备执行 → 设备上报 reported → 平台自动清理已满足的 desired |
| 离线 | desired 持久化到 MySQL,设备上线后平台自动补推 Delta |
3. 清除期望状态
DELETE /blade-iot/shadow/desired
Content-Type: application/json请求体:
{
"productKey": "pk001",
"deviceName": "device001",
"keys": ["switch"],
"version": 5
}| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
| keys | String[] | 否 | 要清除的属性名列表,null 或不传表示清除全部 desired |
| version | Integer | 否 | 期望版本号 |
清除后如果设备在线,平台会推送新的 Delta(可能为空 {},表示无待执行指令)。
4. 清除整个影子
DELETE /blade-iot/shadow/remove?productKey={pk}&deviceName={dn}三级存储(Caffeine / Redis / MySQL)全部清除。设备再上线时视为全新设备。
通过 MQTT 设置 desired
应用也可通过 MQTT 设置期望状态,与 REST API 等价。
发布主题: /blade/shadow/update/{pk}/{dn}
{
"id": "uuid",
"version": "1.0",
"method": "thing.shadow.desired",
"params": {
"desired": { "switch": 0 },
"version": 5
}
}method 区分
设备上报 reported 和应用设置 desired 使用同一个 Topic,通过 method 字段区分:
thing.shadow.update→ 设备上报 reportedthing.shadow.desired→ 应用设置 desired
四、版本控制策略
| version 值 | 含义 |
|---|---|
不传 / null | 跳过版本校验(等价于 -1) |
-1 | 显式跳过版本校验 |
| 正整数 | 必须与当前影子版本一致,否则返回错误码 6300 |
推荐策略:
| 角色 | 建议 | 原因 |
|---|---|---|
| 设备端 | 不传 version(或传 -1) | 简化实现,设备通常是唯一的 reported 写入方 |
| 应用端 | 携带 version | 防止多个应用并发覆盖 desired |
| 旁路同步 | 强制 version=-1 | 避免与设备主动上报产生版本冲突 |
版本冲突处理:
1. 捕获错误码 6300(SHADOW_VERSION_CONFLICT)
2. 重新调用 GET /shadow/detail 获取最新影子文档
3. 基于最新 version 重新提交修改五、调试与排查
影子状态不更新
| 检查项 | 排查方法 |
|---|---|
| 设备是否在线 | 查看设备状态页面或调用设备状态 API |
| 旁路同步是否报错 | 检查日志中是否有 属性上报同步影子失败 警告 |
| Redis 是否正常 | 影子缓存和分布式锁都依赖 Redis |
Delta 推送失败
| 检查项 | 排查方法 |
|---|---|
| 推送日志 | 检查是否有 Delta 推送失败 警告 |
| 设备订阅 | 确认设备已订阅 /blade/shadow/delta/{pk}/{dn} 主题 |
| PubSub 通道 | 确认 Redis PubSub 频道 blade:iot:shadow:delta:push 正常 |
desired 不自动清理
| 检查项 | 排查方法 |
|---|---|
| 类型一致性 | 确认 reported 值与 desired 值类型和值都完全相等 |
| 常见陷阱 | desired 写入字符串 "26",设备上报数字 26 → Objects.equals 返回 false |
| 解决方案 | 确保应用端和设备端的数据类型一致 |
设备上线后没收到 Delta
| 检查项 | 排查方法 |
|---|---|
| 上线事件 | 确认设备上线事件被正确触发(查看 MqttConnectStatusListener 日志) |
| Delta 是否存在 | 通过 REST API 获取影子,检查 delta 字段是否非空 |
| 订阅时序 | 确认设备在连接建立后先订阅 Delta 主题,再进行其他操作 |
六、约束与限制
| 约束项 | 限制值 | 说明 |
|---|---|---|
| 影子文档最大体积 | 64 KB | UTF-8 字节数,超限返回 6302 |
| 属性总数 | 256 个 | reported + desired 合计,超限返回 6304 |
| 单设备写操作频率 | 10 次/秒 | 超限返回 6303 |
| 分布式锁租约 | 5 秒 | 超时返回 6305 |
