最近开发的物联网项目需要将传感器数据实时推送到网页时,项目要求是必须要获取实时数据那么传统的HTTP轮询方案------它笨重、延迟高、服务器压力大着肯定就不行了。最终商议决定使用MQTT ,MQTT协议是一种轻量级和发布/订阅模式成为了完美的解决方案,让前端与设备像好友聊天般自然通信,这样设备上报的数据我们就能实时获取到了!
下面分步介绍一下:
一、MQTT 通讯:像"订阅公众号"一样简单
想象一下关注微信公众号:你订阅(subscribe
)某个频道(Topic
),当公众号发布者(publisher
)推送新内容时,你就能立刻收到。MQTT的核心逻辑正是如此:
先给大家看一下客户端长什么样子,我感觉图像说明永远比文字强

-
核心角色:
- Broker(代理服务器) :消息中转站,类似微信服务器。负责接收消息、存储消息(根据配置)、将消息分发给正确的订阅者。常用开源Broker有
Mosquitto
,EMQX
,HiveMQ
。 - Publisher(发布者) :发送消息的客户端。比如温度传感器、你的后端服务、甚至另一个前端。它向特定
Topic
发送消息。 - Subscriber(订阅者) :接收消息的客户端。你的前端应用就是典型的订阅者。它向Broker注册自己关心的
Topic
。
- Broker(代理服务器) :消息中转站,类似微信服务器。负责接收消息、存储消息(根据配置)、将消息分发给正确的订阅者。常用开源Broker有
-
关键概念:Topic(主题)
- 发布者向某个
Topic
发布消息。 - 订阅者可以订阅精确的Topic (如
$thing/down/property
)
- 发布者向某个
-
通信流程
- 前端(作为订阅者)通过 WebSocket 连接到 MQTT Broker。
- 前端发送
subscribe
命令,告知Broker它想监听哪些Topic
(如sensor/temp
)。 - 温度传感器(发布者)连接到同一个Broker。
- 当温度变化时,传感器向
sensor/temp
这个Topic
发布一条包含新温度值的消息。 - Broker 收到消息,查看所有订阅了
sensor/temp
的客户端(包括我们的前端)。 - Broker 立即将这条温度消息推送给所有订阅了该主题的前端。
- 前端收到消息,解析数据(如
{ "value": 25.5, "unit": "C" }
),然后更新网页上的温度显示。
优势凸显:
- 实时性:消息即时推送,告别轮询延迟。
- 低开销:协议设计精简,节省带宽和设备资源。
- 解耦 :发布者和订阅者互不知晓对方存在,只通过
Topic
交互,系统扩展性、灵活性极佳。 - 一对多广播:一条消息可被多个订阅者同时接收。
二、前端连接 MQTT:实战代码(WebSocket + MQTT.js)
前端使用MQTT通常依赖WebSocket协议建立连接。以下是使用流行库 MQTT.js
的详细步骤和代码。
1. 准备工作
-
引入 paho-mqtt.js :
bashnpm install paho-mqtt --save
或
html<script src="js/paho-mqtt.js"></script>
如果是引入的话,直接网上搜索就行,多的很很容易搜到。我这里是直接引入的。
2. 完整示例代码 (JavaScript)
JavaScript
//订阅消息的topic
const equipment_property_up = `这里换成你自己设置的订阅消息的topic` //设备属性上报
//时间戳
function getData(n) {
n = new Date(n)
return n.toLocaleDateString().replace(/\//g, "-") + " " + n.toTimeString().substr(0, 8)
}
let timestamp = Math.floor(Date.now() / 1000);
// 创建客户端实例
const options = {
clean: true,
connectTimeout: 4000,
clientId: `链接的ClientId,也是校验的id`,
username: equipmentId,
password: topic_password,
}
// 修复Paho初始化方式
var client = new Paho.MQTT.Client(
"mqtt.youcaishu.com",
8084,
"/mqtt",
options.clientId
);
// 更新连接配置
client.connect({
userName: options.username,
password: options.password,
useSSL: true, // 启用SSL
mqttVersion: 4, // MQTT版本
keepAliveInterval: 30,
onSuccess: () => {
console.log('MQTT连接成功');
// 订阅所有主题
[ equipment_property_up ].forEach(topic => {
client.subscribe(topic, {qos: 0});
});
},
onFailure: (err) => {
console.error('MQTT连接失败:', err);
}
});
// 设置回调处理器
client.onConnectionLost = onConnectionLost;
client.onMessageArrived = onMessageArrived;
// 当客户端失去连接时调用
function onConnectionLost(responseObject) {
if (responseObject.errorCode !== 0) {
console.log("onConnectionLost:" + responseObject.errorMessage);
}
}
// 当消息到达时调用
function onMessageArrived(message) {
//获取消息的topic
let topic = message.destinationName;
//获取消息的payload
let payload_json = JSON.parse(message.payloadString)
console.log(payload_json)
const communication_type = payload_json.method //用通讯类型判断'上下行'
let type_txt = ''
if (type == 'control') {
type_txt = '下行'
} else {
type_txt = '上行'
}
let list_html = `
<tr>
<td>${getData(new Date())}</td>
<td>${type_txt}</td>
<td>${topic}</td>
<td>${message.payloadString}</td>
</tr>
`
$('#topic_list').prepend(list_html)
}
3. 关键代码解析
- 连接 mqtt : 使用
Paho.MQTT.Client(url, options)
建立到Broker的WebSocket连接。options
包含客户端ID、连接参数、认证信息等。 - 订阅 (
client.subscribe
) : 连接成功后,调用subscribe(topic, callback)
订阅感兴趣的Topic
。支持通配符订阅。 - 处理消息 (
'message'
事件) : 当消息到达时调用onMessageArrived
获取消息内容 - 状态管理 : 当客户端失去连接时调用
onConnectionLost
4. 测试
运行项目,我们看看效果如何?


三、重要注意事项
-
安全性:
- 生产环境绝对不要使用
ws://
(明文WebSocket) !必须使用wss://
(基于TLS/SSL加密的WebSocket)。 - 务必配置强用户名/密码认证。
- 考虑客户端ID管理、ACL(访问控制列表)限制订阅/发布权限。
- 生产环境绝对不要使用
-
QoS (服务质量等级) : MQTT 提供 0, 1, 2 三种消息传递保证级别。前端应用通常使用
QoS 0
(最多一次,性能最好)或QoS 1
(至少一次,确保送达但可能重复)。在subscribe
和publish
时可以指定。 -
浏览器兼容性:主要依赖 WebSocket。现代浏览器普遍支持良好。老旧浏览器可能需要 polyfill。
-
资源消耗:虽然MQTT轻量,但维持长连接和大量消息处理仍需注意前端性能和内存管理。
四、总结
通过 MQTT 协议,前端应用能够轻松实现与物联网设备、后端服务的高效、实时、双向 通信。其基于 Topic
的发布/订阅模式,天然适合需要实时数据推送的场景(如仪表盘、监控系统、即时聊天、协同编辑)。
paho-mqtt.js
库提供了简洁强大的API,让前端开发者只需几行代码即可接入 MQTT 生态。记住,在拥抱实时性的同时,务必将安全性作为重中之重进行配置。现在,就让你的前端应用"订阅"真实世界的数据流吧!
一个小经验: 在调试MQTT连接问题时,善用浏览器的开发者工具查看WebSocket连接状态、网络流量,并结合Broker的日志(如果可访问)进行排查,往往事半功倍。前端与MQTT的结合,为构建动态、响应式的Web应用打开了新维度。