DNS Round Robin 故障转移不适用于 mqtt.js
Posted
技术标签:
【中文标题】DNS Round Robin 故障转移不适用于 mqtt.js【英文标题】:DNS Round Robin failover doesn't work with mqtt.js 【发布时间】:2019-08-28 09:18:00 【问题描述】:我有一个 NodeJS 应用程序,它使用 mqtt.js 连接到 emqx 集群。
MQTT 集群包含 2 个节点,我尝试为使用 DNS 循环提供故障转移。所以我有 1 个 A-Record(比如说 mqtt.example.com),它指向 2 个 IP(IP1 和 IP2)。当两个节点都在线时,我的 NodeJS 应用程序也可以正常连接并订阅所选主题。
现在在 MQTT 节点上,我可以看到应用程序连接到哪个节点。当我现在停止应用程序连接的节点时,我希望它(迟早)会故障转移到第二个活动节点。
我还使用 Loraserver(连接到 MQTT)以及 MQTT 的 Node Red 实现进行了测试,当我停止它们连接的节点时,它们都立即连接到活动节点。
但是我的带有 mqtt.js 的 NodeJS 应用程序一直在尝试连接到我刚刚停止的节点并且没有尝试连接到活动节点。
场景说明:
-
我有 2 个活动节点,IP1 和 IP2
我将 Loraserver、Node Red 和 NodeJS 连接到 mqtt.example.com
所有 3 个应用程序都连接到 IP1
我通过关闭 emqx 进程来停止 IP1 节点
Loraserver 和 Node Red 会立即自动连接到 IP2
带有 mqtt.js 的 NodeJS 但是不断向我显示错误消息
错误:连接 ECONNREFUSED
使用 IP1 并且不会故障转移到 IP2(让它运行大约 20 分钟,没有发生任何事情。DNS 租用时间设置为 5 分钟,如果有任何相关性)
对于使用 mqtt.js 的应用程序,如何使用 DNS 循环实现故障转移? 感谢您的帮助
编辑:根据要求,添加了测试代码:
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
[...]
【问题讨论】:
鉴于 Node-RED 使用 mqtt.js 库来实现它的 MQTT 节点,这不应该是库问题。编辑问题以显示您正在使用的代码,以便我们查看是否有任何明显的内容。 好吧,不知道。添加了我用于测试的代码。 【参考方案1】:首先,您不应该在on.('reconnect')
回调中调用connect()
。图书馆将为您处理所有这些。
...
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
]
【讨论】:
更改 mqtt_reconnect 会导致相同的错误。我知道输入多个服务器但使用纯 IP 地址而不是 url 的可能性,mqtts 也不再起作用,对吗?所以我必须通过 *** 回退到 mqtt(会工作,但如果我可以让它与 mqtts 一起运行会更好,这样我就不会将 *** 作为单点故障)。 决定采用解决方案,在客户端的服务器部分添加多个服务器,最后通过 *** 公开没有 ssl 的 mqtt。猜猜我可以接受答案,因为到目前为止解决方法工作正常(仍然是 *** 作为 spof 让我烦恼)而且我不知道为什么 DNS 故障转移不起作用以上是关于DNS Round Robin 故障转移不适用于 mqtt.js的主要内容,如果未能解决你的问题,请参考以下文章
集群环境中的故障转移不适用于 JSF 2、Richfaces 4、Tomcat 7
AWS Route53 故障转移和 chrome DNS 缓存