MQTT.js持久会话订阅者断开重连后收到QoS 2重复消息问题
问题原因
核心问题是MQTT协议中QoS 2的「恰好一次」交付是建立在消费端正确向broker发送确认报文的基础上,你当前的订阅端代码缺少手动确认逻辑,默认的自动确认逻辑存在时序漏洞:
- MQTT.js默认开启
autoAck,收到消息后会立刻自动向broker发送确认报文,但如果报文在网络传输过程中丢失、或客户端刚收到消息还没来得及发出确认就断开连接,broker会判定该消息未被成功消费,在你下次重连持久会话时就会重新推送。 - 你当前的
message事件回调参数写法不符合MQTT.js的标准定义,缺少后续手动确认必须的packet对象参数。
修复方案
修改subscriber.js代码,核心改动两点:连接配置开启手动确认、消息处理完成后手动触发确认:
const mqtt = require("mqtt"); const client = mqtt.connect("mqtt://localhost", { port: 1883, clean: false, clientId: "mqttjs_1", // 开启手动确认,关闭自动确认 manualAcks: true }); client.on("connect", () => { client.subscribe("Weather", { qos: 2 }); }); // 修正回调参数:顺序为 topic, 消息 payload, 报文对象 client.on("message", (topic, message, packet) => { const context = message.toString(); console.log(context); // 处理完成后手动发送确认,告知broker该消息已消费 client.ack(packet); });
补充注意点
- 如果你使用的是MQTT.js 3.x版本,配置项改为
autoAck: false即可,手动确认逻辑保持一致。 - 发布端代码中设置了
clean: false但使用随机生成的clientId,会导致broker中积累大量无用的持久会话,如果你不需要发布端的持久会话能力,可将发布端的clean改为true减少broker资源占用。
内容的提问来源于stack exchange,提问作者Niccolò Fant
相关产品推荐
相关产品推荐

