1. 为什么物联网项目都绕不开 MQTT
搞物联网开发的人,迟早会撞上 MQTT 这个协议。不管你是做智能家居、工业采集、车联网还是农业大棚监控,只要涉及设备跟服务器之间的数据往来,MQTT 几乎就是默认选项。我这些年做过的物联网项目里,十个有八个底层通信用的是它,剩下两个要么是 HTTP 轮询(费电、费流量),要么是私有 TCP 协议(维护成本高、生态差)。
MQTT 全称 Message Queuing Telemetry Transport,翻译过来叫消息队列遥测传输。名字听着唬人,本质上就是一个基于发布/订阅模式的轻量级消息协议。你可以把它想象成一个邮局系统:设备把消息投递到某个“主题”上,谁订阅了这个主题,谁就能收到消息。发布者和订阅者互相不认识,全靠中间的 MQTT 服务器(Broker)来转发。这种解耦设计带来的好处非常直接——设备端不需要知道数据发给谁,服务端也不需要知道数据从哪来,双方只关心“主题”这个中间层。
它跑在 TCP/IP 之上,默认端口 1883,加密版本是 8883。协议头最小只有 2 个字节,对于内存只有几十 KB 的单片机来说非常友好。我见过很多刚入行的朋友,一上来就想自己写 TCP 协议做设备通信,结果光处理断线重连、消息去重、QoS 保障就耗掉大半精力,最后项目延期。用 MQTT 的话,这些脏活累活协议本身已经帮你处理了。
这篇文章适合谁看?如果你是物联网方向的学生,正在做毕业设计,需要把传感器数据传到云端;如果你是嵌入式工程师,第一次接触 MQTT,想知道怎么在设备端接入;如果你是后端开发,需要搭建 MQTT 服务器并处理海量设备连接——那这篇内容基本能覆盖你 80% 的需求。我会从协议核心概念讲起,然后带你搭服务器、写客户端、做联调,最后把踩过的坑和排查技巧一并倒出来。
2. MQTT 核心概念拆解与选型思路
2.1 发布订阅模式到底解决了什么问题
传统请求响应模式是这样的:设备采集到温度数据,主动调用服务器的 HTTP 接口把数据推上去。这种方式的问题在于,设备必须知道服务器的地址,而且每次都要建立连接、发送请求、等待响应。一个温度传感器如果每 5 秒上报一次,一天就是 17280 次请求,每次都要三次握手,功耗和流量都很可观。
发布订阅模式把“谁发给谁”这个问题彻底解耦了。设备只管把消息发布到sensor/temperature/room1这个主题上,至于谁需要这个数据——可能是数据库写入服务、可能是告警服务、可能是实时大屏——它们各自订阅这个主题就行。设备端代码里完全不需要出现任何跟“接收方”相关的逻辑。这个设计带来的扩展性提升是巨大的:新增一个数据消费方,不需要动设备端一行代码。
还有一个容易被忽略的好处是消息过滤。MQTT 支持通配符订阅,比如sensor/temperature/#可以匹配所有温度相关主题,sensor/+/room1可以匹配所有传感器类型但限定房间1。这种灵活的过滤机制让服务端架构可以做得非常清晰。
2.2 QoS 等级怎么选才不浪费资源
MQTT 定义了三个服务质量等级,这是新手最容易迷糊的地方。我见过有人在设备端全部用 QoS 2,结果设备功耗飙升,电池三天就没电了。
| QoS 等级 | 含义 | 消息到达保证 | 网络开销 | 适用场景 |
|---|---|---|---|---|
| QoS 0 | 最多一次 | 可能丢失 | 最低 | 高频传感器数据、可容忍丢包的场景 |
| QoS 1 | 至少一次 | 可能重复 | 中等 | 一般指令下发、状态上报 |
| QoS 2 | 恰好一次 | 不丢不重 | 最高 | 计费、关键配置下发 |
选型逻辑很简单:问自己一个问题——这条消息丢了会怎样?如果丢了只是少一个数据点,曲线稍微有个缺口,那 QoS 0 就够了。如果丢了会导致设备状态不一致,那就用 QoS 1,同时在应用层做去重。QoS 2 的握手流程是四次交互,在弱网环境下反而容易因为超时导致连接断开,我一般只在极少数关键场景才用。
注意:QoS 等级是发布者和订阅者分别协商的。发布者用 QoS 1 发,订阅者可以用 QoS 0 收,最终生效的是两者中较低的那个。
2.3 会话保持与遗嘱消息的实战价值
MQTT 有一个clean session标志位(MQTT 5.0 里叫clean start),这个参数决定了 Broker 是否为客户端保存会话状态。如果设为 false,Broker 会记住这个客户端的订阅关系和未确认的消息,等客户端重新连接后继续推送。这个特性在移动网络下非常有用——设备进隧道断网了,出来之后还能收到断网期间的消息。
遗嘱消息(Last Will and Testament)是另一个被低估的功能。设备连接时可以预先设置一条遗嘱消息,当设备异常断开时,Broker 会自动发布这条消息。比如设备连接时设置遗嘱主题为device/status/room1,消息内容为offline,正常上线时发布online。这样服务端就能实时感知设备在线状态,不需要额外做心跳超时判断。我做过一个项目,设备端没有用遗嘱消息,结果服务端判断设备离线要等 3 个心跳周期,延迟很大,后来加上遗嘱消息后状态切换几乎是实时的。
2.4 Broker 选型:Mosquitto、EMQX 还是自己写
市面上 MQTT Broker 选择不少,我按使用场景分个类。
Mosquitto是最轻量的选择,安装包只有几百 KB,单机跑几千个连接没问题。适合开发测试、小型项目、树莓派网关。它的配置文件简单直观,命令行工具mosquitto_pub和mosquitto_sub调试起来很方便。缺点是集群能力弱,官方不支持原生集群。
EMQX是国内用得最多的企业级 Broker,单节点能扛百万连接,支持集群、规则引擎、数据桥接。如果你要做商业项目,设备量上万,直接上 EMQX 不会错。它有个 Dashboard 界面,能看到连接数、消息吞吐、主题列表,排查问题很直观。
HiveMQ和VerneMQ也是不错的选择,前者对 MQTT 5.0 支持最完整,后者基于 Erlang 写的高并发场景表现好。至于自己用 Netty 写一个 Broker——除非你是为了学习协议细节,否则不建议在生产环境这么干,消息持久化、会话管理、集群同步这些坑太深了。
3. 从零搭建 MQTT 开发环境
3.1 Linux 下安装 Mosquitto 并配置认证
我以 Ubuntu 为例,其他发行版命令略有差异。安装很简单:
sudo apt update sudo apt install mosquitto mosquitto-clients安装完成后 Mosquitto 默认只监听 localhost,而且允许匿名连接。生产环境肯定不能这样,我们需要改配置。配置文件在/etc/mosquitto/mosquitto.conf,我一般会新建一个/etc/mosquitto/conf.d/custom.conf,内容如下:
listener 1883 0.0.0.0 allow_anonymous false password_file /etc/mosquitto/passwd persistence true persistence_location /var/lib/mosquitto/ log_dest file /var/log/mosquitto/mosquitto.log逐行解释一下:listener 1883 0.0.0.0让 Broker 监听所有网卡;allow_anonymous false关闭匿名访问;password_file指定密码文件;persistence开启消息持久化,重启后未送达的消息不会丢;log_dest把日志写到文件方便排查。
创建用户和密码:
sudo mosquitto_passwd -c /etc/mosquitto/passwd myuser # 输入两次密码 sudo chown mosquitto:mosquitto /etc/mosquitto/passwd sudo systemctl restart mosquitto注意:
mosquitto_passwd -c的-c参数会覆盖已有文件,第二次添加用户时要去掉-c,否则前面的用户会被清掉。这个坑我踩过,当时排查了半天为什么旧用户连不上。
3.2 Windows 下快速跑通 MQTT 服务
Windows 上我推荐两种方式。一是直接下载 Mosquitto 的安装包,安装后在服务里启动,配置文件在安装目录下。二是用 Docker,一行命令搞定:
docker run -d --name mosquitto -p 1883:1883 -p 9001:9001 \ -v /path/to/mosquitto.conf:/mosquitto/config/mosquitto.conf \ eclipse-mosquitto9001 端口是 WebSocket 用的,如果你要做网页端 MQTT 客户端,需要映射这个端口。Windows 下调试工具我强烈推荐MQTT Explorer,图形界面,能实时看到主题树和消息内容,比命令行直观太多。下载安装后新建连接,填上 Broker 地址和账号密码就能用。
3.3 客户端库选型与最小连接示例
不同语言有不同的 MQTT 客户端库,我列几个常用的:
| 语言 | 库名 | 特点 |
|---|---|---|
| Python | paho-mqtt | 官方推荐,API 简洁 |
| Java | Eclipse Paho | 成熟稳定,Android 也能用 |
| C/C++ | Eclipse Paho Embedded | 适合单片机,资源占用小 |
| JavaScript | mqtt.js | 浏览器和 Node.js 通用 |
| Go | eclipse/paho.mqtt.golang | 并发性能好 |
先用 Python 跑一个最小示例,验证环境是否正常:
import paho.mqtt.client as mqtt def on_connect(client, userdata, flags, rc): print(f"连接结果: {rc}") client.subscribe("test/topic", qos=1) def on_message(client, userdata, msg): print(f"收到消息: {msg.topic} -> {msg.payload.decode()}") client = mqtt.Client() client.username_pw_set("myuser", "mypassword") client.on_connect = on_connect client.on_message = on_message client.connect("127.0.0.1", 1883, 60) client.loop_forever()这段代码做了三件事:连接 Broker、订阅test/topic、收到消息后打印。loop_forever()会阻塞主线程,内部处理心跳和重连。如果你要在主线程做其他事情,可以用loop_start()启动后台线程。
4. 设备端接入与消息收发实战
4.1 主题设计规范:别等设备多了再改
主题设计是 MQTT 项目里最容易被忽视但影响最深远的事情。我见过一个项目,设备端主题是随手写的data1、data2、data3,后来设备增加到几百个,服务端完全没法做批量订阅和权限控制,最后只能推倒重来。
好的主题设计应该遵循几个原则。第一,用/分层,从大到小排列,比如{产品ID}/{设备ID}/{消息类型}。第二,避免用中文和特殊字符,虽然协议支持,但不同客户端库处理起来可能有差异。第三,预留通配符匹配的空间,比如factory/line1/device001/temperature这样的结构,服务端可以用factory/line1/+/temperature订阅整条产线的温度。
我一般推荐的主题结构是这样的:
上行数据:v1/devices/{deviceId}/telemetry 上行事件:v1/devices/{deviceId}/events 下行指令:v1/devices/{deviceId}/commands 设备状态:v1/devices/{deviceId}/statusv1是版本号,方便以后协议升级。telemetry放周期性采集数据,events放告警和异常事件,commands是服务端下发的指令,status用遗嘱消息维护在线状态。这个结构清晰且可扩展,设备量大了之后权限控制也好做——比如只允许设备订阅自己的commands主题。
4.2 消息格式:JSON 还是 Protobuf
消息体格式的选择取决于你的资源约束。JSON 可读性好,调试方便,但体积大。Protobuf 体积小,解析快,但需要预先定义 schema,调试不直观。CBOR 是折中方案,二进制但不需要 schema。
我的经验是:设备资源充足(比如 ESP32、树莓派)就用 JSON,开发效率高;资源紧张(比如 STM32 加 NB-IoT 模组)就用 Protobuf 或自定义二进制格式。一个典型的 JSON 遥测消息长这样:
{ "ts": 1718000000000, "temp": 23.5, "hum": 65.2, "bat": 87 }字段名尽量短,因为每个字节都是流量。ts是时间戳,temp温度,hum湿度,bat电量。如果设备端没有 RTC,时间戳可以由服务端收到消息后补上。
4.3 断线重连与消息缓存策略
网络不稳定是物联网的常态。设备端必须处理断线重连,而且重连后要能恢复订阅、补发缓存消息。paho-mqtt 的loop_forever()自带重连,但重连后订阅关系需要重新建立——如果你用的是clean_session=False,Broker 会保留订阅,但为了保险,我一般在on_connect回调里重新订阅一次。
消息缓存要分情况。如果是 QoS 1 的消息,客户端库内部会缓存未确认的消息,重连后自动重发。但如果是设备主动采集的数据,断网期间的数据需要自己缓存。我的做法是在设备端维护一个环形缓冲区,断网时数据写入缓冲区,重连后按时间顺序补发。缓冲区大小根据断网时长和采集频率来定——假设断网最长 10 分钟,每 5 秒采集一次,那就是 120 条,缓冲区设 200 条留余量。
注意:补发历史数据时,消息里必须带原始时间戳,否则服务端会以为是当前数据。我见过一个项目补发时没带时间戳,结果历史数据全被当成实时数据写入,曲线图直接乱掉。
4.4 用 MQTT Explorer 做联调排查
设备端和服务端都写好了,怎么验证通信正常?MQTT Explorer 是最顺手的工具。打开后连接到 Broker,左侧会显示所有活跃的主题树。你可以在设备端发布一条消息,然后在 Explorer 里看到主题节点闪烁,点进去就能看到消息内容和 QoS 等级。
它还有一个很实用的功能是“发布”面板,可以手动往任意主题发消息,用来测试设备端的指令接收逻辑。比如你往v1/devices/device001/commands发一条{"cmd":"reboot"},看设备端是否响应。这个工具帮我省了大量写测试代码的时间。
5. 常见问题与排查技巧实录
5.1 连接失败排查清单
连接失败是最常见的问题,原因五花八门。我整理了一个排查顺序,按这个流程走基本能定位到问题。
| 现象 | 可能原因 | 排查方法 |
|---|---|---|
| Connection refused | Broker 没启动或端口不对 | netstat -tlnp | grep 1883看端口监听 |
| Connection refused | 防火墙拦截 | telnet broker_ip 1883测试连通性 |
| Not authorized | 账号密码错误 | 用mosquitto_pub命令行验证 |
| Not authorized | ACL 权限限制 | 检查 Broker 的 ACL 配置 |
| 连接后立即断开 | clientId 冲突 | 确保每个设备 clientId 唯一 |
| 连接后立即断开 | 心跳超时 | 检查 keepalive 设置和网络延迟 |
clientId 冲突这个问题特别隐蔽。MQTT 协议规定,如果两个客户端用同一个 clientId 连接,Broker 会把前一个踢掉。我见过一个项目,设备端 clientId 写死成device001,结果批量生产时所有设备都用同一个 ID,上线后互相踢,设备状态疯狂闪烁。后来改成用 MAC 地址或芯片唯一 ID 作为 clientId 才解决。
5.2 消息丢失的三种可能
消息丢了,先别急着怀疑 Broker。按这个顺序查:第一,发布时 QoS 是不是 0?QoS 0 本来就不保证到达,丢了正常。第二,订阅时的 QoS 是不是比发布时低?如果发布用 QoS 1,订阅用 QoS 0,Broker 转发时可能降级。第三,clean_session是不是 true?如果是 true,设备断线期间的消息会被丢弃。
还有一种情况是主题不匹配。MQTT 主题是大小写敏感的,Sensor/Temp和sensor/temp是两个不同的主题。通配符#只能放在最后,sensor/#/temp这种写法是非法的。+可以放在中间,但只能匹配一层,sensor/+/temp能匹配sensor/room1/temp,但匹配不了sensor/room1/sub/temp。
5.3 性能调优的几个关键参数
当设备量上来之后,Broker 的默认配置可能扛不住。以 Mosquitto 为例,这几个参数值得调整:
max_connections 10000 max_queued_messages 1000 max_inflight_messages 20 message_size_limit 1048576max_connections是最大连接数,根据服务器内存来定,每个连接大约占几 KB 到几十 KB。max_queued_messages是每个客户端的消息队列长度,设太小会导致 QoS 1 消息被丢弃,设太大吃内存。max_inflight_messages是同时发送未确认的消息数,设大了吞吐高但容易在弱网下拥塞。message_size_limit限制单条消息大小,防止有人发超大消息把 Broker 打爆。
EMQX 的话,这些参数在 Dashboard 里都能调,而且有实时监控曲线,调完能立刻看到效果。我一般会先压测一轮,用emqtt_bench模拟几千个客户端同时连接和收发,观察 CPU、内存、消息延迟的变化,再决定参数怎么调。
5.4 安全加固:别让设备变成跳板
MQTT 安全加固分几层。传输层用 TLS,8883 端口,设备端配置 CA 证书。认证层用用户名密码或客户端证书,别开匿名访问。授权层用 ACL 控制每个设备只能发布和订阅自己的主题。
ACL 配置示例(Mosquitto):
user device001 topic write v1/devices/device001/telemetry topic write v1/devices/device001/events topic read v1/devices/device001/commands这样 device001 只能往自己的遥测和事件主题写,只能读自己的指令主题。即使设备被破解,攻击者也没法订阅其他设备的数据或往其他主题发消息。这个配置在设备量大的时候尤其重要,我见过因为没配 ACL,一个设备被攻破后攻击者往全局主题发垃圾消息,导致整个系统瘫痪的案例。
6. 从单机到集群的扩展思路
6.1 什么时候需要集群
单台 Mosquitto 跑几千个连接没问题,但到了几万甚至几十万连接,就需要集群了。判断依据有几个:CPU 持续超过 70%、内存占用超过 80%、消息延迟明显增加、单点故障不可接受。如果只是设备量增长但消息频率低,先考虑垂直扩容(加 CPU、加内存),比集群简单得多。
集群方案我推荐 EMQX,它原生支持集群,节点之间自动同步路由和会话信息。部署方式可以用 Docker Compose 快速搭一个三节点集群做测试:
version: '3' services: emqx1: image: emqx/emqx:5.0 environment: - EMQX_CLUSTER__DISCOVERY_STRATEGY=static - EMQX_CLUSTER__STATIC__SEEDS=[emqx@emqx1,emqx@emqx2,emqx@emqx3] ports: - "1883:1883" - "18083:18083"三个节点都配同样的 seeds,启动后会自动组成集群。Dashboard 的 18083 端口能看到集群状态和节点列表。
6.2 桥接与规则引擎的实用场景
EMQX 的规则引擎可以在消息到达时触发动作,比如写入数据库、转发到 Kafka、调用 HTTP 接口。我做过一个项目,设备数据需要同时写入时序数据库和推送到前端大屏,用规则引擎配置两条规则,一条写 InfluxDB,一条转发到 WebSocket,完全不需要写代码。
桥接功能可以把消息从一个 Broker 转发到另一个 Broker。比如边缘网关本地跑一个 Mosquitto,通过桥接把数据同步到云端 EMQX。这样即使云端断连,边缘侧的数据也不会丢,网络恢复后自动补发。配置桥接时注意主题前缀的设置,避免消息回环——云端往边缘发消息时加个cloud/前缀,边缘往云端发时加edge/前缀,两边规则里排除对方的前缀。
6.3 监控告警体系搭建
生产环境必须要有监控。EMQX 暴露了 Prometheus 格式的指标接口,用 Prometheus 抓取后用 Grafana 展示。关键指标包括:当前连接数、消息收发速率、消息丢弃数、连接失败率、平均延迟。我一般会设几条告警规则:连接数突降超过 20% 触发告警(可能 Broker 挂了或网络故障)、消息丢弃数持续大于 0 触发告警(QoS 队列满了)、连接失败率超过 5% 触发告警(认证或网络问题)。
日志也要收集。Mosquitto 的日志在/var/log/mosquitto/mosquitto.log,EMQX 的日志在log/目录下。用 Filebeat 或 Fluentd 收集到 Elasticsearch,配合 Kibana 做检索。设备端的问题往往需要结合 Broker 日志和设备日志一起看,比如设备说“我发了”,Broker 日志里没有,那就是网络问题;Broker 日志里有但订阅方没收到,那就是订阅关系或 QoS 问题。
7. 几个真实项目的经验沉淀
7.1 低功耗场景的 MQTT 参数调优
做过一个 NB-IoT 水表项目,设备靠电池供电,要求续航 5 年。MQTT 的心跳间隔直接决定功耗——心跳越频繁,设备唤醒次数越多,耗电越快。默认 keepalive 是 60 秒,对水表来说太频繁了。我们把 keepalive 调到 300 秒,同时把 QoS 降到 0,数据上报周期设为 6 小时一次。这样设备大部分时间在休眠,只有上报时才唤醒连接。
但 keepalive 调大有风险:如果设备在 keepalive 时间内没有发送任何消息,Broker 会认为设备离线并发布遗嘱消息。所以要么保证设备在 keepalive 周期内至少发一次心跳,要么把 keepalive 设得比上报周期还长。我们最后用的是 300 秒 keepalive,设备每 6 小时上报一次,中间靠 Broker 的遗嘱消息来维护在线状态——设备休眠时显示离线,上报时显示在线,业务上完全可以接受。
7.2 弱网环境下的消息可靠性保障
地下停车场、偏远山区、移动中的车辆,这些场景网络质量很差。除了 QoS 1 和消息缓存,还有一个技巧是应用层确认。设备发出消息后,服务端处理完往一个确认主题回一条消息,设备收到确认后才把本地缓存的消息标记为已发送。如果超时没收到确认,重发。
这个机制会增加消息量,但在弱网下能显著提升可靠性。实现时注意确认消息也要带唯一 ID,设备端维护一个待确认消息的字典,收到确认后删除对应条目。超时重发要有次数限制,比如重试 3 次后放弃并记录日志,避免无限重试把网络堵死。
7.3 设备固件升级的 MQTT 实现
OTA 升级用 MQTT 做指令下发和进度上报很合适。流程是这样的:服务端往v1/devices/{deviceId}/commands发一条升级指令,包含固件版本号和下载地址。设备收到后开始下载,期间往v1/devices/{deviceId}/events上报下载进度。下载完成后校验固件完整性,然后重启切换分区。
关键点是分批升级。别一次性给所有设备发升级指令,否则服务器带宽会被打满,而且万一固件有问题会导致大面积设备变砖。我的做法是按设备分组,每组 5%,观察一组升级成功后再推下一组。升级指令里带一个随机延迟,让设备在不同时间点开始下载,避免同时请求。
注意:升级指令的 QoS 要用 1,确保设备一定能收到。进度上报用 QoS 0 就行,丢几条无所谓。固件下载地址最好用 HTTPS,下载完成后必须校验 MD5 或 SHA256,防止固件被篡改。
7.4 物联网毕业设计的选题建议
如果你是在做毕业设计,MQTT 相关的题目通过率很高,但容易做得太泛。我的建议是找一个具体场景深入做,比如“基于 MQTT 的实验室环境监测系统”,功能包括:ESP32 采集温湿度光照,通过 MQTT 上报到 EMQX,服务端用 Python 订阅消息写入 SQLite,前端用 WebSocket 实时展示,异常时发邮件告警。这个工作量适中,技术栈清晰,答辩时也好讲。
加分项可以加:设备端用 TLS 加密、服务端做 ACL 权限控制、用 Docker Compose 一键部署、加一个简单的设备管理界面。别贪多,把 MQTT 的 QoS、遗嘱消息、会话保持这几个特性用上并讲清楚原理,比堆一堆花哨功能但每个都浅尝辄止要好得多。
8. 工具链与调试技巧速查
8.1 命令行工具的高频用法
mosquitto_pub和mosquitto_sub是调试利器,我几乎每天都会用。订阅所有主题:
mosquitto_sub -h 127.0.0.1 -p 1883 -u myuser -P mypassword -t '#' -v-v参数会打印主题名,不加的话只显示消息内容。发布一条消息:
mosquitto_pub -h 127.0.0.1 -u myuser -P mypassword \ -t 'v1/devices/device001/commands' \ -m '{"cmd":"reboot"}' -q 1如果要测试遗嘱消息,可以用mosquitto_sub订阅状态主题,然后用mosquitto_pub连接时设置遗嘱参数,再强制杀掉进程,观察状态主题是否收到离线消息。
8.2 抓包分析 MQTT 报文
有时候需要看协议层面的交互,比如确认 QoS 握手是否正常。用 Wireshark 抓包,过滤器输入mqtt就能看到所有 MQTT 报文。CONNECT、CONNACK、PUBLISH、PUBACK、SUBSCRIBE、SUBACK 这些报文类型都会显示出来。如果看到 PUBLISH 后没有 PUBACK,说明 QoS 1 的确认丢了,可能是网络问题或 Broker 没响应。
Wireshark 还能看到报文里的具体字段,比如 keepalive 值、clientId、用户名、主题名、QoS 等级。调试认证问题时特别有用——你能看到客户端发的用户名到底是什么,是不是编码问题导致乱码。
8.3 压力测试与容量规划
上线前一定要做压测。emqtt_bench是 EMQX 官方出的压测工具,可以模拟大量客户端连接和消息收发:
emqtt_bench sub -h 127.0.0.1 -p 1883 -c 5000 -t 'bench/%i' -q 1 emqtt_bench pub -h 127.0.0.1 -p 1883 -c 100 -t 'bench/%i' -q 1 -s 128 -I 10第一条命令模拟 5000 个订阅者,第二条模拟 100 个发布者,每个发布者每秒发 10 条 128 字节的消息。观察 Broker 的 CPU、内存、消息延迟,找到瓶颈后再调整参数。容量规划时留 30% 余量,别让 Broker 跑满,否则突发流量会导致消息堆积。
9. 写在最后
MQTT 这个协议,入门容易精通难。表面上看就是连上 Broker、订阅主题、收发消息,但真正要做好一个物联网项目,需要考虑的东西远不止这些。主题设计、QoS 选型、断线重连、消息缓存、安全加固、集群扩展、监控告警——每一个环节都有坑,每一个坑我都亲自踩过。
我个人的体会是,前期多花时间把主题结构和 QoS 策略定好,后期能省掉大量重构的功夫。设备端代码要尽量简单,把复杂性留给服务端,因为设备端升级成本太高了。调试时善用 MQTT Explorer 和 Wireshark,很多问题看一眼报文就清楚了,比猜来猜去快得多。
最后分享一个小技巧:如果你在本地开发,可以用 Docker 起一个 EMQX,Dashboard 的默认账号是 admin,密码是 public,登录后能直观地看到所有连接和主题,对理解 MQTT 的工作机制很有帮助。等本地跑通了再上服务器,能避免很多环境问题。