AqaraLink 开发者平台入门:从零到第一次调用
AqaraLink 开发者平台入门:文档列出的步骤、三种授权模式、虚拟账户、App Key 和设备接入途径。
简要回答 — Aqara 的消息推送服务有两条获取途径:推送到你配置的地址的 HTTP 推送,以及 MQ(文档称其基于开源的 RocketMQ 构建)。HTTP 途径会被定期验证并记录失败统计,执行规则是真实的:如果推送失败率在 5 分钟内超过 5%,你会收到短信或电子邮件通知,通知发出半小时后推送将被暂停,直到你在控制台中重新启用。两种途径都保留最近 12 小时的消息。每条消息都带有
msgId和毫秒级时间戳,请以此建立幂等性和对账机制,并把顺序当作需要自己处理的问题,而不是想当然的保证。
这篇文章能防止你的集成悄悄失效。可靠性为百分之九十九的推送集成,在演示时看起来完美,到第二个月却会破坏客户的数据。
文档对两种方式及其原因说得很直接:满足消息实时性和消息持久性的要求。
| HTTP 推送 | MQ | |
|---|---|---|
| 形式 | 平台向你登记的地址 POST JSON | 平台向队列发布消息;你订阅并消费 |
| 保留窗口 | 最近 12 小时内推送失败的消息,可通过 API 查询 | MQ 中保留最近 12 小时的消息供消费 |
| 投递耦合 | 你的端点必须在线且响应快 | 由你的消费者确认 |
| 故障可见性 | 平台定期轮询你的地址并统计失败率 | 英文文档未说明 |
| 最适合 | 可以水平扩展的常规 Web 服务 | 希望有缓冲的大批量设备 |
对大多数集成商来说,HTTP 是默认选择。当你每台设备的事件量让 12 小时的 HTTP 失败窗口成为过于单薄的保障,或者你的消费者本来就是队列消费者时,再考虑 MQ。请注意两者的不对等:对 HTTP,文档描述了明确的验证和失败追踪机制;对 MQ,文档描述了保留和订阅,但没有对等的执行规则。如果你为了可靠性选择 MQ,不要假设它会以同样的方式自我修复。
以下是消息推送页面里明确说明的事实:
application/json,发往在 Console → Project Management → Message push settings 下配置的地址。token(被授权账户的有效访问令牌)、time 和 nonce(随机数,用于保证唯一性)。可选:appkey 和 sign,仅当你在该页面用 appKey 和 appSecret 启用签名验证时才会返回。appkey、nonce、token、time 按 ASCII 排序 → 拼接成 appkey=xxx&nonce=xxx&time=xxx&token=xxx → 附加 appSecret → 转为小写 → MD5 32 位。英文文档中没有说明、依赖之前值得先向 Aqara 核实的事项:至少一次与至多一次的投递语义、设备之间的顺序保证、POST 失败时的重试策略和重试次数,以及非 2xx 响应是否计入 5% 的统计。设计时要假设每条消息都可能被重复、延迟、乱序和丢失。
设备状态是电平触发的。门传感器报告的是打开,而不是一个边沿。同一状态重发并不是错误,而把每条消息都当作新消息的消费者会把所有事情重复计数。
平台给了你所需的东西。每条消息都带有 msgId(消息唯一标识 id);time(消息生成时的时间戳,单位为毫秒);openId(被授权用户标识);eventType;以及 data,其内部有自己的毫秒级 data.time。把 msgId 存入带唯一约束的表,在消费者的入口处丢弃重复消息,并且把插入和状态写入放在同一个事务里,因为在事务之外写入的去重表会骗你。
会收到三类消息,需要不同的处理:
gateway_bind、subdevice_bind、gateway_unbind、unbind_sub_gw)、上线与离线(gateway_online、gateway_offline、subdevice_online、subdevice_offline)、dev_name_change、dev_position_assign,以及规则事件 linkage_created、scene_created、event_created 及其对应的 _deleted。文档说明这些全部会推送到第三方服务器,你无法过滤。文档明确指出设备数据量巨大,你可以收窄接收的内容。有两个订阅接口,都要求先配置消息推送:
config.resource.subscribe:资源列表,每项包含 subjectId、resourceIds 数组和可选的 attach 字符串。spec.config.trait.subscribe:trait 数组,每项包含 deviceId、codePaths 数组(文档规定的格式为 endpointId.functionCode.traitCode)以及可选的 attach。即使不需要,attach 字段也值得使用。它会原样透传到通知内容中,是放置你自己的关联数据的天然位置:哪个订阅、哪个租户、哪台逻辑设备。按租户订阅,而不是全局订阅:在一个有 2,000 个单位的场所里,如果每个消费者都接收每一条属性消息,那是你自己制造的成本和延迟问题。
比较 time 与 data.time。两者都是毫秒时间戳,但含义不同:外层的是消息生成的时间,内层的是具体事件的时间戳。乱序投递就出现在这个差距里:人体移动传感器触发,第一次触发还在传输时第二次触发到达,clear 先于 occupancy 的设置到达。按到达顺序应用它们的消费者,最终会认为一个空房间有人。在没有文档记载的顺序保证下:
data.time 实现“后写者胜出”,而不是到达顺序。把当前状态连同其时间戳一起保存,拒绝比你手中数据更旧的更新。如果拿不到保证,你也不需要保证,你需要的是一个修复循环。安排周期性的全量状态读取,读取你关心的设备,并覆盖本地视图。推送流在你宕机、乱序或去重不当时出错的一切,全量读取都能修正。
它的频率要能限制“看得见的错误”的时间窗口,同时成本可承受:对楼宇管理叠加层,每个空间每隔几分钟全量扫描一次是站得住脚的,对消费者 App 每小时一次就够了。推送失败消息 12 小时的保留窗口在这里是个优势:让对账先查询该失败查询接口,因为重放被丢弃的消息,比重新读取一切便宜。
文档记载的失败路径很明确,而且有牙齿:5 分钟内失败率超过 5%,会通过短信或电子邮件通知你;通知发出半小时后,推送被暂停,直到你在控制台重新启用。 一次二十分钟的部署,就可能让你的集成悄悄失去订阅,唯一的警报还发到一部没人看的手机上。
防御措施,按价值排序:
msgId 唯一约束与状态变更在同一个事务内attach 做关联data.time 为准,而不是到达顺序