“数据在传输过程中怎么保证不丢?”
这个问题最容易得到的回答是重试、ACK、MQTT QoS 1。但它们只解决了链路中的一小段。
一条设备数据从采集端到平台,通常要经过采集、内存队列、协议客户端、网络、Broker、消费者和数据库。任何一段都可能失败:程序刚采到数据就崩溃,发送成功但确认包丢了,消费者处理完还没提交位点,或者数据库已经写入但上游误以为失败再次发送。
因此,可靠传输的第一个现实是:丢失和重复往往是一对矛盾。
只要允许超时重试,就可能重复;如果为了绝不重复而不敢重试,又可能丢失。多数工程系统采用“至少一次投递”,再通过幂等把重复数据消掉。
我更习惯按下面的顺序检查一条链路:
- 数据产生后是否先进入可恢复的本地队列,而不是只放在内存里?
- 发送方是否等到明确确认,超时后是否有边界地重试?
- 消息是否带稳定的业务 ID、设备 ID 和采集时间?
- 消费方是处理成功后再确认,还是一收到就确认?
- 数据库能否用唯一键或幂等表抵抗重复写入?
- 队列积压、重试次数和死信数据能否被观察到?
MQTT QoS 1 能保证客户端与 Broker 之间“至少一次”,却不能证明 Broker 后面的业务已经落库。Publish 返回也不等于整个业务闭环成功。真正可靠的确认点,必须尽量靠近最终结果。
还有一种常见误区,是无限重试。下游长期故障时,它会把旧数据、连接和 goroutine 一起拖住。重试需要退避、上限和死信出口;重要数据则需要持久化补偿任务,让在线链路和恢复链路分开。
所谓不丢,不是找一个参数打开,而是为每个失败窗口准备答案。最后通常得到的也不是数学意义上的“绝不丢”,而是一套可确认、可重放、可去重、可追查的机制。
TLS 解决的是另一类问题
可靠性关心数据能否到达,TLS 关心数据在路上是否被窃听、篡改,以及通信双方究竟是谁。两者经常同时出现在 MQTT 配置里,却不能互相替代。
给转发服务接入双向证书时,通常会配置 CA 证书、客户端证书和客户端私钥。TLS 握手会协商加密算法、验证身份并生成本次会话的密钥。普通 HTTPS 多由客户端验证服务端;双向 TLS 还要求服务端验证客户端证书。对边缘网关来说,这意味着平台和设备侧应用都要证明自己的身份。
连接成功也只说明加密通道建立了。真正上线时,还需要注意:
- 不使用
InsecureSkipVerify绕过证书校验; - 校验服务端名称,避免任意合法证书冒充目标服务;
- 私钥不进入代码仓库,配置严格的文件权限;
- 证书有签发、轮换、吊销和过期告警;
- 不同环境或设备组使用不同身份;
- TLS 之后继续做 Topic 权限和业务鉴权。
把视角再拉远,数据安全覆盖的是完整生命周期。采集时确认设备身份和数据来源;传输时做加密、签名和防重放;存储时做权限隔离、备份与脱敏;查询和导出时继续鉴权并留下审计记录。备份销毁和数据删除也不能漏掉。
TLS 更像运输过程中的锁。它不能证明数据来源一定可信,也不能限制数据到达平台后被谁使用。可靠性和安全性都需要沿着完整链路思考,不能只在架构图上补一个 QoS 参数或小锁图标。