首页 / 博客 / 开发者与集成商
开发者与集成商

Aqara 推送订阅与 Webhook 的生产环境设计

服务器机房走廊,事件流被可视化为在互联的智能家居设备之间流动的数据线

简要回答 — 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,不要假设它会以同样的方式自我修复。

平台有记载的内容,以及没有记载的内容

以下是消息推送页面里明确说明的事实:

  • 投递方式是 HTTP POST,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 位。
  • 地址会不时被验证,以确保服务地址和消息接收响应机制的可靠性。
  • 失败阈值:5 分钟内失败率超过 5%,会通过短信或电子邮件通知第三方。若未解决,将在通知半小时后暂停推送。请在控制台中重新启用。
  • 推送失败的消息保留最近 12 小时,并提供查询 API。

英文文档中没有说明、依赖之前值得先向 Aqara 核实的事项:至少一次与至多一次的投递语义、设备之间的顺序保证、POST 失败时的重试策略和重试次数,以及非 2xx 响应是否计入 5% 的统计。设计时要假设每条消息都可能被重复、延迟、乱序和丢失。

幂等性:假设会有重复

设备状态是电平触发的。门传感器报告的是打开,而不是一个边沿。同一状态重发并不是错误,而把每条消息都当作新消息的消费者会把所有事情重复计数。

平台给了你所需的东西。每条消息都带有 msgId(消息唯一标识 id);time(消息生成时的时间戳,单位为毫秒);openId(被授权用户标识);eventType;以及 data,其内部有自己的毫秒级 data.time。把 msgId 存入带唯一约束的表,在消费者的入口处丢弃重复消息,并且把插入和状态写入放在同一个事务里,因为在事务之外写入的去重表会骗你。

会收到三类消息,需要不同的处理:

  1. 事件通知消息:设备生命周期事实:绑定与解绑(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。文档说明这些全部会推送到第三方服务器,你无法过滤。
  2. 设备属性消息:状态变化和操作触发,例如开关状态、负载功率和用电量,按用户选择的订阅模式推送。
  3. 设备控制失败消息:控制失败时返回,附带触发来源、触发时间和错误码。这是你所发出的控制未生效时唯一可靠的信号。

订阅:收窄范围,否则被淹没

文档明确指出设备数据量巨大,你可以收窄接收的内容。有两个订阅接口,都要求先配置消息推送:

  • 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%,会通过短信或电子邮件通知你;通知发出半小时后,推送被暂停,直到你在控制台重新启用。 一次二十分钟的部署,就可能让你的集成悄悄失去订阅,唯一的警报还发到一部没人看的手机上。

防御措施,按价值排序:

  • 快速返回 2xx。 先确认,再处理,在处理程序后面接一个队列。
  • 在平台的通道上告警,不只是你自己的。 短信/电子邮件会在暂停之前到达。把它转给能在几分钟内采取行动的人。
  • 监控你自己的丢弃率。 5 分钟错误率接近 5%,无论有没有人注意到,你都已进入执行窗口。
  • 自动化重新启用的流程,或在需要之前写好操作手册。被暂停的推送只是控制台上的一个开关,而凌晨 2 点时,这个开关就是整个事故。
  • 在产品里明显地失败。 界面显示“最后更新于 4 小时前”,胜过没人看的警报。

上线前检查清单

  • [ ] 已启用并测试签名验证,或有意不启用
  • [ ] msgId 唯一约束与状态变更在同一个事务内
  • [ ] 订阅按租户划定范围,而不是全局;用 attach 做关联
  • [ ] “后写者胜出”以 data.time 为准,而不是到达顺序
  • [ ] 控制失败消息转发到有人会看到的地方
  • [ ] 已安排并核算周期性全量状态对账
  • [ ] 失败率警报设在低于 5%/5 分钟的安全余量处
  • [ ] 有重新启用被暂停推送的操作手册
  • [ ] 投递保证、顺序、重试策略和速率限制已书面向 Aqara 确认

正在规划项目?

请告诉我们您的空间情况。我们的企业业务团队将在一个工作日内回复,提供建议方案和报价。

WhatsApp 联系我们 →
[email protected]
+603-5880 5486