DNS Round Robin故障转移不适用于mqtt.js

时间:2019-08-28 09:18:00

标签: node.js dns mqtt failover

我有一个使用mqtt.js连接到emqx集群的NodeJS应用程序。

MQTT群集包含2个节点,我尝试提供这些节点以使用DNS Round Robin。因此,我有1个A记录(例如mqtt.example.com),它指向2个IP(IP1和IP2)。当两个节点都在线时,我的NodeJS应用程序也可以正常连接并订阅选定的主题。

现在在MQTT节点上,我可以看到应用程序连接到哪个节点。现在,当我停止与应用程序连接的节点时,我希望它(迟早会)故障转移到第二个活动节点。

我还使用了Loraserver(已连接到MQTT的连接器)以及MQTT的Node Red实施进行了测试,并且当我停止它们所连接的节点时,它们都立即连接到了活动节点。

但是我的带有mqtt.js的NodeJS应用程序一直尝试连接到我刚刚停止的节点,而不尝试连接到活动节点。

场景说明:

  1. 我有2个活动节点,IP1和IP2
  2. 我将Loraserver,Node Red和NodeJS连接到mqtt.example.com
  3. 所有3个应用程序都连接到IP1
  4. 我通过关闭emqx进程来停止IP1节点
  5. Loraserver和Node Red将立即自动连接到IP2
  6. 带有mqtt.js的NodeJS但是不断向我显示错误消息
  

错误:连接ECONNREFUSED

具有IP1且不会故障转移到IP2(将其运行约20分钟,什么也没发生。如果有任何关联,则将DNS租约时间设置为5分钟)

如何为使用mqtt.js的应用程序使用DNS Round Robing实现故障转移? 感谢您的帮助

编辑:根据要求,添加了测试代码:

const mqtt = require('mqtt');
const tls = require('tls');
const MQTTTOPIC = 'test/upload';
const BROKER_URL = 'tls://mqtt.example.com';
const BROKER_PORT = '8883';
const MQTTUSER = 'username';
const MQTTPASS = 'password';

var mqttoptions = {
    clientId: MQTTUSER,
    port: BROKER_PORT,
    keepalive: 60,
    username: MQTTUSER,
    password: MQTTPASS
};

var client = mqtt.connect(BROKER_URL, mqttoptions);
client.on('connect', mqtt_connect);
client.on('reconnect', mqtt_reconnect);
client.on('error', mqtt_error);
client.on('message', mqtt_messsageReceived);
client.on('close', mqtt_close);

function mqtt_connect() {
    console.log("Connecting MQTT");
    client.subscribe(MQTTTOPIC, mqtt_subscribe);
}

function mqtt_subscribe(err, granted) {
    console.log("Subscribed to " + MQTTTOPIC);
    if (err) { console.error(err); }
}

function mqtt_reconnect(err) {
    console.log("Reconnect MQTT");
    if (err) { console.error(err); }
    client = mqtt.connect(BROKER_URL, mqttoptions);
}

function mqtt_error(err) {
    console.error("MQTT Error!");
    if (err) { console.error(err); }
}

function after_publish() {
    //do nothing
}

function mqtt_close() {
    console.warn("Close MQTT");
}

function mqtt_messsageReceived(topic, message, packet) {
    console.log("Message: " + message + " --- Received on Topic " + topic);
}

编辑2: 万一重要,我将在pm2上运行代码

编辑3: 加上完整的日志输出:

17|LOCALTE | Connecting MQTT
17|LOCALTE | Subscribed to test/upload
17|LOCALTE | Close MQTT
17|LOCALTE | Reconnect MQTT
17|LOCALTE | Error: connect ECONNREFUSED IP1:8883
17|LOCALTE |     at Object.exports._errnoException (util.js:1034:11)
17|LOCALTE |     at exports._exceptionWithHostPort (util.js:1057:20)
17|LOCALTE |     at TCPConnectWrap.afterConnect [as oncomplete] (net.js:1096:14)
17|LOCALTE | MQTT Error!
17|LOCALTE | { Error: connect ECONNREFUSED IP1:8883
17|LOCALTE |     at Object.exports._errnoException (util.js:1034:11)
17|LOCALTE |     at exports._exceptionWithHostPort (util.js:1057:20)
17|LOCALTE |     at TCPConnectWrap.afterConnect [as oncomplete] (net.js:1096:14)
17|LOCALTE |   code: 'ECONNREFUSED',
17|LOCALTE |   errno: 'ECONNREFUSED',
17|LOCALTE |   syscall: 'connect',
17|LOCALTE |   address: 'IP1',
17|LOCALTE |   port: 8883 }
17|LOCALTE | Close MQTT
17|LOCALTE | Reconnect MQTT
[...]

1 个答案:

答案 0 :(得分:1)

首先,您不应该在connect()回调中调用on.('reconnect')。该库将为您处理所有这一切。

...
function mqtt_reconnect(err) {
    console.log("Reconnect MQTT");
    if (err) { console.error(err); }
}
...

如果将其删除后仍然无法使用,作为解决方法,您可以列出一组服务器,库将自行轮流运行。

var mqttoptions = {
    clientId: MQTTUSER,
    port: BROKER_PORT,
    keepalive: 60,
    username: MQTTUSER,
    password: MQTTPASS
    servers:[
        {
            protocol: 'mqtts',
            host: 'ip-address-1',
            port: 8883
        },
        {
            protocol: 'mqtts',
            host: 'ip-address-2',
            port: 8883
        }
    ]
}