MQTT、HTTP、CoAP 在物联网项目中怎么选
MQTT、HTTP、CoAP 怎么选?从主题设计、QoS、遗嘱消息、指令回执到 4G 弱网心跳,结合冷链、BMS、避雷器监测等真实项目讲清物联网通信协议选型和 MQTT 系统落地要点。

本文目录
物联网项目选通信协议,问题通常不出在参数表,更多出在系统真正跑起来以后。设备怎么上线,平台要不要长期感知在线状态,现场网络稳不稳,后面谁来接住排障和维护,这几件事比单次传输效率更早决定结果。前期判断到位,设备接入、消息流转和 APP 控制会顺很多;前期判断失真,麻烦多半会落到掉线补偿、控制延迟和线上排查。
MQTT、HTTP、CoAP 常被放进同一张对比表,可企业项目很少靠“哪个参数更漂亮”拍板。设备形态、交互频率、控制要求和部署环境,往往先把范围收住,协议选择只是顺着这些条件往下落。做判断时,最好把 物联网开发服务、MQTT物联网系统开发 和 物联网APP开发服务 的交付边界一起看,再回到 航标远程监测与运维平台案例 这类项目里,看看设备接入、平台联动和后续维护到底是怎么落地。
先把通信场景说清楚
如果设备需要长期在线,平台还要随时下发控制指令,MQTT 往往更合适。它适合处理持续连接、发布订阅、状态同步这类要求,设备、服务端和 APP 也更容易围着同一套消息模型协同工作。智能家居、环境监测、远程设备管理这类项目,前期讨论常停在“数据能不能上来”,到了交付阶段,大家更在意设备数量上去以后消息通道稳不稳,控制指令能不能及时到位,异常状态能不能尽快被发现。
HTTP 更适合请求响应关系清楚、设备不用常在线,或者现有系统已经围着 Web API 组织起来的项目。它的好处也很直白,团队熟悉、调试方便、首版推进通常更快。可设备一多、上报频率一高、控制动作开始变密,轮询、重试、状态补偿这些负担就会慢慢压到业务层。项目最开始看着只是多写几个接口,后面却可能长成一套越来越重的兼容逻辑。
CoAP 更常见于资源更紧、网络条件更受限的终端。它能承担轻量消息传输,但企业项目里很少因为看见“CoAP”三个字就直接拍板。团队还是得回到终端芯片能力、网关位置、接入网络、云端入口这些现实条件上。很多时候,难点不在 CoAP 本身,难在后面谁来把它和网关、平台、日志、监控一起接住,出了问题有没有成熟的排查手段。
协议选择的第一步,不必急着给三者排高低,先把项目到底在传什么、怎么传、谁来收、谁来控说清楚。高频上报和偶发请求不是一回事,平台主动下发和设备定时同步也不是一回事。规模只差一个数量级,后面的架构压力就可能完全不同。
再看网络条件和后期维护
很多团队在立项阶段先算开发速度,却把现场网络和维护成本放到后面。网络稳定、设备数量不大、消息也不密的时候,HTTP 当然能很快起步;可项目进入弱网、移动网络、跨地域部署,或者需要断线重连、消息补偿、在线保活后,复杂度就不会老老实实待在接口层。它会一路往上影响设备逻辑、平台状态、APP 交互和运维告警。
MQTT 的价值,往往就体现在这里。在线、订阅、推送、重连这些事情,本来就适合提前纳入通信模型。后面做设备状态、批量接入、实时控制和多端联动时,团队不用临时在业务层补那么多结构。对于已经明确要做双向通信、远程控制和消息分发的项目,先看 MQTT物联网系统开发 的交付边界,通常比单看协议说明更接近真实情况。
CoAP 的账要换一种算法。它确实可能帮终端省资源,但项目总成本不能只看终端这一段。要是最后还要经过网关汇总、统一上云、统一鉴权和统一监控,协议层节省下来的那部分,不一定能直接换成更低的总体维护成本。很多团队后面吃力,不是某个数据包传不过去,问题出在整条链路缺少统一治理,出了问题只能一段一段排。
通信协议一旦进了正式交付,回头改的代价通常不低。主题结构怎么定义,鉴权怎么做,消息失败怎么补,日志留到什么粒度,监控阈值怎么设,这些都不该等到上线后再一点点补。前面省下来的时间,很容易变成后面的联调时间和线上故障处理时间。
我们项目里的 MQTT
我们做过的几个平台都用 MQTT 做设备和云端之间的通道。冷链平台用 EMQX 接 4G DTU,指令下发后按设备和寄存器匹配回执;BMS 平台的原始报文经 MQTT 进入协议桥接服务再入库;灯具控制器里走 TCP 私有协议的老设备,由 Go 桥接服务转成 MQTT。App 的实时刷新,有的项目用 WebSocket。更多见技术实力。
一张图看 MQTT 物联网系统的分层
MQTT 物联网系统分层架构:设备层、接入层、消息处理、存储层、应用层
MQTT 在系统里只负责「设备和服务端之间的消息通道」,它不解决协议解析、存储和业务逻辑。我们交付的 MQTT 平台基本都按上图分五层:
- 设备层:MCU 直连 4G DTU 或 Wi-Fi 模组,网关型设备再挂子设备。设备只需要实现连接、心跳、上报和订阅下行主题。
- 接入层:Broker 用 EMQX 居多,开 TLS(8883 端口),每台设备独立的用户名密码或证书,并用 ACL 限制它只能发布、订阅自己的主题。
- 消息处理层:服务端用共享订阅(
$share/group/...)把消息分给多个处理实例,负责解析二进制 / Modbus / JSON 报文、匹配指令回执、执行告警规则。 - 存储层:业务数据放 PostgreSQL,曲线数据放时序库(如 TDengine),在线状态放 Redis,原始报文单独留存,方便事后排查。
- 应用层:App 不直接连 Broker,而是通过业务 API + WebSocket 拿数据,权限统一由服务端控制。
MQTT 主题怎么设计
主题设计决定了权限能不能做细、后期能不能扩展。推荐按「租户 / 产品 / 设备 / 方向 / 类型」分级,上下行分开:
{tenant}/{product}/{deviceId}/up/telemetry # 周期遥测
{tenant}/{product}/{deviceId}/up/event # 事件、告警
{tenant}/{product}/{deviceId}/up/cmd_ack # 指令回执
{tenant}/{product}/{deviceId}/down/cmd # 下行指令
{tenant}/{product}/{deviceId}/status # 在线状态(遗嘱 + retain)对应的 ACL 规则很简单:设备只能 publish 自己的 up/# 和 status,只能 subscribe 自己的 down/#。这样即使某台设备的密钥泄露,也拿不到别人的数据。
几条实践经验:
- 不要把设备 ID 放在 payload 里而主题里不带。主题带 ID 才能做 ACL,也方便在 Broker 上按设备排查。
- 上行和下行一定分开,否则设备订阅了自己发布的主题,会收到自己的消息造成死循环。
- 主题里不要放会变化的业务字段(例如所属客户),设备转手后主题就对不上了;归属关系放在平台数据库里维护。
- 设备已经定型、只能用固定主题时(我们做 BMS 远程升级时就是这样,模组固定订阅
Ctrl/.../序列号、发布reply/.../序列号),服务端适配设备,而不是要求改固件。
QoS、保留消息和遗嘱:最容易用错的三个参数
| 参数 | 含义 | 建议用法 |
|---|---|---|
| QoS 0 | 至多一次,不确认 | 高频遥测(丢一帧不影响曲线) |
| QoS 1 | 至少一次,可能重复 | 指令、告警、事件;接收方要按消息 ID 去重 |
| QoS 2 | 恰好一次,四次握手 | 设备端实现成本高,物联网项目很少真正需要 |
| retain | Broker 保留该主题最后一条消息 | 只用于「在线状态」这类最新值主题,遥测不要开 |
| LWT 遗嘱 | 设备异常断开时 Broker 代发 | 发布 status=offline,配合 retain 做在线判定 |
| Keep Alive | 心跳间隔,超过约 1.5 倍未收到报文即判定断开 | 4G 设备要小于运营商 NAT 超时,兼顾功耗 |
很多项目把 QoS 2 当成「更可靠」直接开满,结果设备端内存和流量都吃紧。更务实的做法是 QoS 1 + 业务层幂等:每条指令带唯一 cmdId,设备收到重复的 cmdId 不再执行,只重发回执。
指令下发和回执:可靠性在业务层闭环
远程控制指令的下发与回执时序:App、业务 API、EMQX、设备
PUBACK 只说明「Broker 把消息送到了设备的网络栈」,不代表设备真的执行了。所以远程控制要在业务层再做一次确认:
- 业务 API 生成
cmdId,写入「待确认指令表」,再发布到down/cmd; - 设备执行完发布
up/cmd_ack,带上cmdId和结果码; - 服务端按 设备 + 指令 ID(寄存器类设备按 设备 + 寄存器地址)精确匹配回执,更新状态并推送给 App;
- 超时未回执则按策略重发或标记失败,App 上显示「设备未响应」而不是一直转圈。
我们做冷链平台时,Modbus 控制器的指令回执就是按「设备 + 寄存器」精确匹配的;做 BMS 远程升级时,每个 1KB 分片都要等设备应答才发下一片,失败累计超过上限直接中止并记录停在哪一片。
4G 设备的心跳和离线判定
4G 设备经过运营商 NAT,长时间没有流量连接会被静默回收,设备以为自己在线,平台却已经收不到消息。处理思路:
- Keep Alive 设得比 NAT 超时短,常见取值在一到几分钟之间,具体要在目标运营商网络下实测;
- 遗嘱消息负责「异常断开」,但 NAT 静默回收时 Broker 也要等 Keep Alive 超时才能发现,所以平台再加一层「超过 N 个上报周期没数据即离线」的定时检查(我们在避雷器监测平台里默认 900 秒);
- App 显示的在线状态以平台判定为准,不要只看数据库里某个字段——我们在灯具控制器项目里就专门处理过「数据库显示在线、实际已断开」的对账问题。
和 HTTP、CoAP 的技术差异
| 对比项 | MQTT | HTTP | CoAP |
|---|---|---|---|
| 连接模型 | 长连接,发布 / 订阅 | 短连接,请求 / 响应 | UDP,请求 / 响应 + Observe |
| 最小报文头 | 2 字节固定头 | 文本头部,通常几百字节 | 4 字节固定头 |
| 服务端主动下发 | 原生支持 | 需轮询、长轮询或 WebSocket | Observe 订阅 |
| 弱网表现 | 断线重连 + 会话保持 | 每次重新建连 | UDP 需应用层重传 |
| 典型场景 | 远程控制、实时状态、告警 | 低频上报、对接业务系统 | NB-IoT、电池供电传感器 |
一句话总结:要双向、要实时、设备长期在线,就用 MQTT;只是偶尔上报,HTTP 足够;终端资源极其受限、走 NB-IoT,再考虑 CoAP。
企业项目里常见的判断方式
智能家居、设备远程控制、状态同步、告警推送这一类场景,优先看 MQTT 往往更稳妥。原因不复杂,这类项目对长期在线和双向通信的依赖本来就高,通信层承担的也不只是“上传成功”,还包括在线感知、状态同步和后续扩展。像 航标远程监测与运维平台案例 这种多点位接入项目,真正决定体验的,通常就是消息稳定性和后续管理成本。
如果项目更偏向设备偶发上报、接口逻辑简单,或者首期目标是先把业务流程打通,HTTP 仍然是很常见的起步方式。它并不落后,只是更适合结构简单、实时性压力不高的阶段。团队如果已经预见到后面设备规模会继续放大,最好在立项时就把升级空间算进去,别把后期迁移压力留到业务已经跑起来以后。
CoAP 适合进入候选,但不适合因为“轻”就被默认选中。终端能力、网关方案、平台接入方式和后期运维安排,最好一起判断。协议本身只是链路中的一段,真正决定项目难度的,还是整套接入和维护方式能不能接稳。
真正落到项目决策时,先把四件事整理出来会更快:设备类型、在线方式、消息频率、控制要求。四项信息齐了,再判断协议是否需要长期在线、是否要双向控制、是否要配套消息中间件和监控体系,讨论就不会一直停在概念层。若你正在做这一步,可以先看 FAQ 里的常见问题,再到 获取技术方案 说明项目背景,我们可以按设备接入方式和业务节奏给出更贴近交付的通信架构建议,避免项目做到一半再重开通信层。
常见问题
MQTT 和 HTTP 哪个更省流量?
长期在线、频繁上报的场景下 MQTT 更省:连接只建立一次,每条消息固定头最小 2 字节;HTTP 每次请求都带完整的文本头部。偶尔上报一次的设备,两者差别不大。
MQTT 的 QoS 应该选几?
遥测用 QoS 0,指令、告警、事件用 QoS 1,并在业务层用消息 ID 去重。QoS 2 对设备端要求高,物联网项目里很少需要。
怎么判断 MQTT 设备掉线?
三层配合:Keep Alive 超时由 Broker 判定断开;遗嘱消息在异常断开时发布离线状态;平台再按「超过若干上报周期没有数据」做定时检查,覆盖 NAT 静默回收这类 Broker 发现不了的情况。
EMQX 和 Mosquitto 怎么选?
Mosquitto 轻量、适合单机和小规模;需要集群、规则引擎、认证插件和管理界面时选 EMQX。我们的生产项目大多用 EMQX。
App 能不能直接连 MQTT Broker?
技术上可以(MQTT over WebSocket),但不建议。App 直连意味着要在 Broker 上为每个用户维护权限,而且业务逻辑会分散在客户端。更稳妥的做法是 App 走业务 API 和 WebSocket 推送,由服务端统一鉴权。
设备固件已经定型,主题不规范怎么办?
服务端适配设备。桥接服务订阅设备现有主题,转换成平台内部的规范主题或直接解析入库,设备侧不用改。
准备做工业控制器IoT平台,建议先把这些信息收齐
信息越完整,方案判断、报价区间和排期就越接近真实交付情况。
- 控制器协议、参数模型、状态字段和异常码文档
- 蓝牙首装、MQTT 远程控制和桥接服务的职责边界
- 告警、日志、趋势曲线、OTA 和群组控制验收范围
- 样机、固件版本、测试环境和部署架构要求
