大多数 IoT 图只有三个方框:设备、一个云图标和一个仪表板。真正重要的问题都藏在它们之间。broker 如何知道是哪台设备在通信?某个站点断网一小时会发生什么?哪些消息可能丢失或被投递两次?一条命令如何在成千上万个传感器中准确送达其中一个?
IoT 架构图应该在图上回答这些问题。本文以一个具体场景,即冷库的温度监控为例绘制这样一张图,并逐一说明每一跳背后的决策:MQTT 及其服务质量等级、主题设计、设备身份、离线站点与事件时间,以及数据最终落在哪里。
IoT 系统的分层
| 层 | 职责 | 在示例中 |
|---|---|---|
| 设备 | 感知或执行动作;通常资源受限、靠电池供电 | 冷冻柜传感器和门传感器 |
| 边缘网关 | 使用本地协议通信,离线时缓存数据,再向上游发布 | 站点网关 |
| 接入 | 认证设备,并按主题路由消息 | MQTT broker |
| 流传输 | 供众多消费者使用的持久、可重放日志 | Kafka |
| 处理 | 聚合、规则和数据补全 | 流处理器 |
| 存储 | 供查询的近期数据,供重放的原始数据 | 时序存储、原始数据归档 |
| 应用 | 仪表板、告警、设备管理 | 仪表板、告警、设备管理 |
并不是每个系统都需要所有分层。带蜂窝或 Wi-Fi 模块的设备通常不经网关,直接向 broker 发布消息。画出你实际运行的分层即可。
为什么用 MQTT
MQTT 是一种发布/订阅协议,客户端连接到 broker,并通过主题交换消息。自 2019 年 3 月起成为 OASIS 标准的 MQTT 5.0 规范将它描述为轻量、开放、易于实现的协议。IANA 为不带 TLS 的 MQTT 注册了 1883 端口,为基于 TLS 的 MQTT 注册了 8883 端口。凡是离开站点的流量都应该使用 8883。
每条消息发布时都带有一个服务质量(QoS)等级:
| QoS | 保证 | 适用场景 |
|---|---|---|
| 0 | 最多一次,无确认 | 高频读数,下一条可以替代丢失的那条 |
| 1 | 至少一次,有确认,可能重复 | 大多数遥测数据和命令,配合幂等的消费者 |
| 2 | 恰好一次,四步握手 | 下游无法处理重复的少数情况 |
QoS 作用于每一跳,而不是端到端:订阅者收到消息时的 QoS,是消息发布时的 QoS 与其订阅被授予的 QoS 中较低的那个。在 QoS 1 下,broker 可能投递重复消息,所以每个消费者都要能承受同一条读数收到两次。设备生成的消息 ID 或每个传感器的时间戳可以让这件事变得简单。
MQTT 5 增加了几项值得画在图上的特性:
- 保留消息(retained)。 broker 保存某个主题上的最后一条消息,并发送给新的订阅者,适合表示设备的当前状态。
- 遗嘱消息(will)。 当客户端的连接在没有正常断开的情况下中断时,broker 会发布该客户端的遗嘱消息,这样网关就能宣告自己已离线。
- 消息过期。 如果消息在开始投递前就已过期,broker 会将其删除。把它用于只在一段时间内有效的命令。
- 共享订阅。 多个消费者共享一个订阅,每条消息只投递给其中一个,这样通往流平台的桥接器就能横向扩展。
主题设计
主题是分层的,订阅者用 + 匹配一层,用 # 匹配其余所有层。像 site/{site}/device/{id}/telemetry 这样的结构,让你可以订阅一个站点、一台设备或全部内容,也让 broker 能把每台设备限制在它自己的主题内。以 $ 开头的主题保留给 broker 使用,以通配符开头的过滤器不会匹配它们。
主题只用于路由和权限。把模式版本和测量时间放进消息负载,这样它们发生变化时也不会破坏所有订阅。
设备身份
为每台设备或网关签发一张由你的设备 CA 签名的独立 X.509 证书,并让 broker 强制要求客户端证书。证书主体成为客户端 ID,broker 据此授予主题权限。这样,被盗的设备可以单独吊销,成千上万台设备的固件里也不会存放共享密码。决定证书何时签发(出厂时还是首次启动时)以及如何在到期前轮换,并把 CA 画在图上。
离线站点与事件时间
站点会断网。在本地缓存读数的网关,会在链路恢复时一次性发送一小时的数据,于是数据会迟到且乱序。为每条读数打上测量时的时间戳,保持网关时钟同步,并按事件时间处理,同时为迟到数据留出余量。像“冷冻柜十分钟内高于 -18 °C”这样的告警,必须根据读数的测量时间来计算,而不是根据到达时间。同样的思路适用于任何事件驱动架构。
示例:冷库监控
每个仓库大约有四十个冷冻柜传感器,每 30 秒上报一次,还有十几个门传感器,在状态变化时上报。站点网关通过本地无线电收集读数,离线时缓存,并通过双向 TLS 以 QoS 1 发布到 MQTT broker。broker 根据设备 CA 校验每张客户端证书。一个使用共享订阅的桥接器把遥测数据复制到 Kafka。一个消费者把原始消息归档到对象存储;流处理器按事件时间计算一分钟聚合值,写入供仪表板使用的时序存储,并发出温度告警。设备管理通过 broker 回传命令和配置。
图表内容
- Warehouse site
- Freezer sensors ×40
- Door sensors ×12
- Site gateway
- MQTT broker
- Device CA
- Device management
- Kafka
- Raw archive
- Stream processor
- Time-series store
- Dashboards
- Temperature alerts
- every 30 s
- on change
- telemetry, QoS 1
- commands
- checks client certificates
- commands, config
- bridge
- raw messages
- aggregates
- rules
这张图的读法如下:
- 左侧,站点。 传感器只与站点网关通信;对 broker 来说,这个站点只有网关这一个客户端。
- 中间,broker。 遥测数据以 QoS 1 向上传输。虚线连线是下发到站点的命令,以及根据设备 CA 进行的证书校验。
- 右侧,数据路径。 Kafka 为原始数据归档和流处理器供数,流处理器把聚合值写入时序存储并发出告警。
- Markdown 块写明了主题结构、各主题的 QoS 和身份规则。
需要标注什么
- 在站点到 broker 的链路上标注协议、端口和 QoS。
- 每类设备的消息速率。 四十个传感器每 30 秒上报一次,就是每个站点每分钟 80 条消息;在确定 broker 规模之前,先乘以站点数量。
- 身份: 用哪张证书、哪个 CA,以及证书如何轮换和吊销。
- 缓存: 网关在离线时能保存多长时间的数据。
- 每个存储的保留期,从流中的数小时到归档中的数年。
- 命令路径, 包括消息过期,以及设备收到未知命令时会怎么做。
常见错误
- 设备之间共用凭据。 一个泄露的密码就会打开所有设备。每台设备使用一张证书。
- 在互联网上开放 1883 端口。 没有 TLS,读数和凭据都以明文传输。
- 默认恰好一次投递。 QoS 1 可能投递重复消息;让消费者具备幂等性,而不是到处使用 QoS 2。
- 按到达时间处理。 缓存的数据会触发误报,或者掩盖真实告警。
- 设备直接写数据库。 你会失去 broker 提供的认证、缓存和扇出能力。
- 没有更新路径。 固件更新和配置变更也是架构的一部分;把它们画出来。
- 给设备通配符权限。 能订阅
#的设备可以读取其他所有设备的数据。
网络拓扑图介绍了站点自身的网络,这部分通常不会出现在 IoT 图中。
在 Archboard 中构建
- 使用 Google 或 GitHub 登录,并新建一张图。
- 为站点添加一个基础节点包中的 框架。在其中为每组传感器添加一个网络节点包中的 设备,类型设为 IoT / 智能家居;再添加一个 设备 表示站点网关,类型设为 调制解调器 / 网关。
- 添加 C4 模型节点包中的 容器 节点,分别表示 MQTT broker、设备管理和流处理器,再添加一个 数据库 表示时序存储。
- 添加安全节点包中的一个 证书,表示设备 CA。
- 添加数据节点包中的 Kafka 和一个 对象存储库,用于原始数据归档。添加 DevOps 节点包中的 指标 表示仪表板,以及一个 告警 节点。
- 从左到右连接它们,为上行链路标注 QoS,把命令和证书校验画成虚线,并添加一个基础节点包中的 Markdown 块,写明主题设计。
这个示例共用 14 个图表节点,在 Free 套餐的限制之内。网络图页面展示了网络节点包中的其余节点。
常见问题
IoT 设备应该用 MQTT 还是 HTTP? MQTT 会保持一条打开的连接,所以云端可以推送命令,而且每条消息的开销很小。对于很少上报、也从不接收命令的设备,HTTP 更简单。
应该使用哪个 QoS 等级? 大多数遥测数据和命令使用 QoS 1,并配合能容忍重复的消费者。可以承受丢失的高频读数使用 QoS 0。只有在下游无法处理重复时才使用 QoS 2。
已经有 MQTT broker 了,还需要 Kafka 吗? 不一定。broker 把消息路由给已连接的订阅者;它不是一个可以重放的长期日志。当多个消费者需要同一份数据,或者你需要重新处理历史数据时,再加入流平台。
设备如何获得证书? 在出厂时或首次启动时通过配置服务获得,每台设备都有自己的密钥对。如果设备带有安全硬件,就把私钥保存在其中。
规则应该在边缘还是云端运行? 在故障期间也必须生效的规则,例如门没关时的本地报警,在网关上运行。需要多个站点数据的规则在云端运行。