You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

NextJS中WSS MQTT连接刚连到Broker就断开并无限重连

问题分析

你的代码核心问题出在useEffect的依赖和清理逻辑上:

  1. 当client状态从null变为MQTT客户端实例时,useEffect会重新执行,此时进入if(client)分支绑定事件监听器,同时在cleanup函数里调用client.end()——这直接导致刚建立的连接被主动关闭,触发无限重连。
  2. 每次useEffect执行都会重复绑定事件监听器,造成冗余甚至逻辑冲突。
  3. 手动实现的ping逻辑和MQTT内置的keepAlive机制重复,可能引发Broker的异常判断。
修复方案

修改后的代码如下,关键调整点已标注:

"use client";
import * as mqtt from "mqtt";
import React, { createContext, useEffect, useState } from "react";

interface MQTTContextType {
  client: mqtt.MqttClient | null;
  clientID: string;
}

export const MQTTContext = createContext({} as MQTTContextType);

const MQTT_URL = `wss://${process.env.NEXT_PUBLIC_EMQX_HOST}:${process.env.NEXT_PUBLIC_EMQX_PORT}/mqtt`;

export default function MQTTProvider({
  clientID,
  children,
}: {
  clientID: string;
  children: React.ReactNode;
}) {
  const [client, setClient] = useState<mqtt.MqttClient | null>(null);

  // 把options放到useEffect外部,避免每次渲染重新创建
  const MQTTOptions = {
    clientId: clientID,
    username: process.env.NEXT_PUBLIC_EMQX_USERNAME,
    password: process.env.NEXT_PUBLIC_EMQX_PASSWORD,
    keepAlive: 30,
    // 可选:添加reconnectPeriod控制重连间隔,避免过快重连
    reconnectPeriod: 5000,
  };

  useEffect(() => {
    // 只在client不存在时创建连接,事件绑定逻辑放在连接创建时执行,避免重复绑定
    if (!client) {
      const newClient = mqtt.connect(MQTT_URL, MQTTOptions);

      // 一次性绑定所有事件监听器
      newClient.on("connect", () => {
        console.log("connected");
        newClient.subscribe("test-mqtt", (err) => {
          if (err) {
            console.log("error subscribing to test-mqtt topic", err);
          }
        });
      });

      newClient.on("error", (err) => {
        console.log("error", err);
      });

      newClient.on("reconnect", () => {
        console.log("reconnecting");
      });

      newClient.on("close", () => {
        console.log("closed");
      });

      newClient.on("disconnect", () => {
        console.log("disconnected");
      });

      newClient.on("end", () => {
        console.log("ended");
      });

      newClient.on("message", (topic, message) => {
        console.log("message received", topic, message.toString());
      });

      newClient.on("packetsend", (packet) => {
        console.log("packet sent", packet);
      });

      newClient.on("packetreceive", (packet) => {
        console.log("packet received", packet);
      });

      // 移除手动ping,依赖MQTT内置的keepAlive机制即可
      setClient(newClient);

      // 组件卸载时关闭连接
      return () => {
        newClient.end(true); // true表示强制关闭,避免等待
      };
    }

    // 当clientID变化时,销毁旧连接并重置状态
    return () => {
      if (client) {
        client.end(true);
        setClient(null);
      }
    };
  }, [clientID]); // 只依赖clientID,避免client状态变化触发重复执行

  const contextValue = {
    client,
    clientID,
  };

  return (
    <MQTTContext.Provider value={contextValue}>{children}</MQTTContext.Provider>
  );
}
关键修改说明
  • 调整useEffect依赖:只监听clientID变化,避免client状态更新触发重复执行,防止频繁创建/销毁连接。
  • 事件监听器一次性绑定:在创建client实例时直接绑定所有事件,避免重复绑定导致的逻辑混乱。
  • 移除手动ping逻辑:MQTT库的keepAlive参数已经会自动维护心跳,手动ping会造成冗余,甚至可能被Broker判定为异常。
  • 优化清理逻辑:组件卸载或clientID变化时,主动关闭旧连接并重置client状态,确保资源正确释放。
  • 添加reconnectPeriod:可选配置,控制重连间隔,避免短时间内频繁重连给Broker造成压力。

内容的提问来源于stack exchange,提问作者vmeesala

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.10 18:46:16