MQTT.js 是一个开源的 MQTT 协议的客户端库,使用 JavaScript 编写,主要用于 Node.js 和 浏览器环境中。是目前 JavaScript 生态中使用最为广泛的 MQTT 客户端库。
https://github.com/mqttjs/MQTT.js
由于 JavaScript 单线程特性,MQTT.js 是全异步 MQTT 客户端,MQTT.js 支持 MQTT/TCP、MQTT/TLS、MQTT/WebSocket,在不同运行环境支持的度如下:
- 浏览器环境:MQTT over WebSocket(包括微信小程序、支付宝小程序等定制浏览器环境)
- Node.js 环境:MQTT、MQTT over WebSocket
安装
// 安装 MQTT 模块
npm install mqtt --save
// 导入 MQTT 模块
const mqtt = require("mqtt");
// 创建 MQTT 客户端并连接到 MQTT 代理
const client = mqtt.connect("mqtt://test.mosquitto.org");
// 当连接成功建立时的回调函数
client.on("connect", () => {
// 订阅主题 "presence"
client.subscribe("presence", (err) => {
if (!err) {
// 当连接建立后,发布消息到主题 "presence"
client.publish("presence", "Hello mqtt");
}
});
});
// 当接收到消息时的回调函数
client.on("message", (topic, message) => {
// 将消息从缓冲区(Buffer)转换为字符串并打印出来
console.log(message.toString());
// 关闭 MQTT 客户端连接
client.end();
});
输出:
Hello mqtt
如果你想运行自己的 MQTT 代理,你可以使用 Mosquitto 或 Aedes-cli,并启动它。你也可以使用一个测试实例:test.mosquitto.org。如果你不想安装一个单独的代理,你可以尝试使用 Aedes。
导入样式:
CommonJS(Require)
// 导入 MQTT 模块
const mqtt = require("mqtt");
// 创建客户端并连接到 MQTT 代理
const client = mqtt.connect("test.mosquitto.org");
ES6 模块(Import)
别名通配符导入
// 导入 mqtt 模块中的所有内容,并赋予命名空间 "mqtt"
import * as mqtt from "mqtt";
// 创建客户端并连接到 MQTT 代理
let client = mqtt.connect("mqtt://test.mosquitto.org");
导入单独的组件
// 从 mqtt 中导入 connect
import { connect } from "mqtt";
// 创建客户端并连接到 MQTT 代理
let client = connect("mqtt://test.mosquitto.org");
命令行工具:
MQTT.js 包含一个与代理交互的命令,为了在全局环境中可用,你应该全局安装 MQTT.js:
npm install mqtt -g
然后,在一个终端中运行:
mqtt sub -t 'hello' -h 'test.mosquitto.org' -v
在另一个终端中运行:
mqtt pub -t 'hello' -h 'test.mosquitto.org' -m 'from MQTT.js'
查看 mqtt help <command>
来获取命令的帮助信息。
调试日志:
MQTT.js 使用 debug 包进行调试。要启用调试日志,需要在运行时设置以下环境变量:
#(使用 PowerShell 举例,默认为 VS Code)
$env:DEBUG='mqttjs*'
关于重连:
任何 WebSocket 连接的重要部分是在连接中断并且客户端需要重新连接时要执行的操作。MQTT 具有内置的重连支持,可以配置以适应应用程序的需求。
刷新身份验证选项/使用 transformWsUrl 进行签名 URL(仅适用于 WebSocket):
当 MQTT 连接中断并且需要重新连接时,通常需要保持与连接相关的任何身份验证与底层身份验证机制的当前状态同步。例如,某些应用程序可能会在初始连接时使用连接选项传递身份验证令牌,而其他云服务可能要求每次连接都要使用签名的 URL。
在重新连接发生在应用程序生命周期中时,原始身份验证数据可能已过期。
为了解决这个问题,我们可以使用一个称为 transformWsUrl
的钩子,在重新连接时操作连接 URL 或客户端选项。
示例(在每次重新连接时更新 clientId 和 username):
const transformWsUrl = (url, options, client) => {
client.options.username = `token=${this.get_current_auth_token()}`;
client.options.clientId = `${this.get_updated_clientId()}`;
return `${this.get_signed_cloud_url(url)}`;
}
const connection = await mqtt.connectAsync(<wss url>, {
...,
transformWsUrl: transformUrl,
});
现在,每当建立新的 WebSocket 连接(希望不会太频繁)时,都会获取到一个新的已签名 URL 或最新的身份验证令牌数据。
注意:目前此钩子不支持 promises,这意味着为了使用最新的身份验证令牌,您必须运行一些外部机制来处理应用程序级别的身份验证刷新,以便 WebSocket 连接可以简单地获取到最新的有效令牌或已签名的 URL。
启用重新连接的 reconnectPeriod 选项:
要确保 MQTT 客户端在连接中断时自动尝试重新连接,必须将客户端选项 reconnectPeriod 设置为大于 0 的值。值为 0 将禁用重新连接,并在连接中断时终止最终连接。
默认值为 1000 毫秒,这意味着在丢失连接后 1 秒后将尝试重新连接。
关于主题别名管理:
启用自动主题别名功能:
如果客户端设置选项 autoUseTopicAlias: true
,那么 MQTT.js 会自动使用现有的主题别名。
示例场景:
- 发布主题 't1',主题别名为 1(注册)
- 发布主题 't1',主题别名为空,主题别名为 1(根据最近的映射自动使用现有映射)
- 发布主题 't2',主题别名为 1(注册覆盖)
- 发布主题 't2',主题别名为空,主题别名为 1(根据最近的映射自动使用现有映射)
- 发布主
题 't1'(t1 不再映射到主题别名 1)
用户不需要管理哪个主题映射到哪个主题别名。如果用户想要注册主题别名,然后发布主题时使用主题别名。如果用户想要使用主题别名,然后发布主题时自动使用现有的映射。
启用自动主题别名功能:
const connection = await mqtt.connectAsync(<server url>, {
...,
autoUseTopicAlias: true,
});
获取已注册的主题别名映射:
如果您想要获取已注册的主题别名映射,可以使用以下命令:
const topicAliasMap = connection._client._topicAliasMap;
console.log(topicAliasMap);
这会打印出已注册的主题别名映射。请注意,这是一个内部属性,可以根据需要进行调试或监视。
使用已注册的主题别名映射:
如果您想要使用已注册的主题别名映射,可以手动为要发布的消息设置主题别名,如下所示:
const topicAlias = connection._client._topicAliasMap.get("your_topic");
const message = "Your message";
const options = {
properties: {
topicAlias: topicAlias,
},
};
connection.publish("your_topic", message, options, () => {
console.log("Message published with topic alias");
});
这将使用已注册的主题别名映射来发布消息。
请注意,_client
是 MQTT.js 的内部客户端对象,不建议在生产代码中直接访问这些内部属性,但它们可以用于调试和测试目的。
```