1

我有一个在 websockets 上运行并部署到 Cloud Run 的 Node JS MQTT 服务器。此服务器使用 Aedes MQTT 代理库 ( https://github.com/moscajs/aedes )。客户端通过端口 443 连接到该服务器。一切正常,但每个客户端都会定期与 MQTT 代理断开连接(大约每 5 分钟,因为每个客户端首次连接)当服务器部署到专用机器或Google Cloud VM,仅在部署到 Google Cloud Run 时发生。

为了说明这个问题,我在 NodeJS 中编写了一个超级简单的 MQTT 服务器,它允许任何客户端连接并回显到队列“响应”客户端发送到队列“消息”的任何消息和一个 NodeJS 脚本来测试它。您可以在https://github.com/madomingo/mqtt_test/tree/main中找到完整的 2 文件项目

服务器代码是:

const aedes = require('aedes')()
const ws = require('websocket-stream')
const port = 8882
const WEB_SOCKETS = true
let httpServer 

if (WEB_SOCKETS) {
    httpServer = require('http').createServer()
    ws.createServer({ server: httpServer }, aedes.handle)
} else {
    httpServer = require('net').createServer(aedes.handle)
}

httpServer.listen(port, function () {
    let type = (WEB_SOCKETS) ? "WebSockets" : "HTTP"
  console.log('STARTED: ' + type + ' server listening on port ', port)
})


aedes.subscribe('messages', function (packet, callback) {
    let msg = packet.payload.toString()
    console.log('Received message', msg);
    aedes.publish(
        {            
            topic: 'responses',
            payload: msg,
            retain: false
        }
    );

});

我还有一个 NodeJS 测试脚本,它连接到服务器并每 15 秒将日期时间发送到队列:

let mqtt = require('mqtt');

// Web sockets host
const host = "ws://localhost:8882"

let clientId = 'test web client ' + Date.now
let client = mqtt.connect(host, {    
    clientId: clientId
  });

client.on('connect', function () {
  console.log("Connected to server")
  client.subscribe('responses');
  sendMessage()
  
  setInterval(() => {
      sendMessage()
  }, 15000);
 

})

client.on('message', function (topic, message) {
  console.log('Received a message in topic: ' + topic + ": " + message.toString());
});

client.on('error', function(e) {
  console.log('Received an error: ' + e.message)
})

client.on('disconnect', function() {
  console.log('Client was disconnected ' )
})
client.on('reconnect', function() {
  console.log('Client was reconnected ')
})

client.on('close', function() {
  console.log('Client was closed ' )
})

client.on('offline', function() {
  console.log('Client was offline ')
})

function sendMessage() {
  let now = new Date().toISOString()
  let msg = "Current time = " + now
  console.log("Publishing data", msg)
  client.publish('messages', msg);
}

如果我在本地运行服务器和测试脚本,一切都按预期工作:每 15 秒,客户端将日期时间发送到队列“消息”中的服务器,然后服务器立即回显这个日期时间到队列“响应”。服务器和客户端都登录到控制台。这无限期地运行并且没有断开连接。

我发现的问题是当服务器部署到 Google Cloud Run 服务时(我已经这样做了,我得到了 url:test-mqtt-server-kdtisjwi5a-ew.a.run.app)。

在测试脚本中,我将主机 url 更改为:const host = "wss://test-mqtt-server-kdtisjwi5a-ew.a.run.app:443"。然后再次运行测试脚本,客户端连接到部署到 Cloud Run 的服务器,一段时间后,控制台按预期显示日志:

Connected to server
Publishing data Current time = 2021-09-16T16:06:26.937Z
Received a message in topic: responses: Current time = 2021-09-16T16:06:26.937Z
Publishing data Current time = 2021-09-16T16:06:41.944Z
Received a message in topic: responses: Current time = 2021-09-16T16:06:41.944Z

但是在第一次连接后每隔 5 分钟,客户端会意外断开连接,尽管它会立即重新连接。日志现在显示:

Received a message in topic: responses: Current time = 2021-09-16T16:11:11.975Z

Client was offline
Client was closed

Client was reconnected
Connected to server
Publishing data Current time = 2021-09-16T16:11:28.121Z
Received a message in topic: responses: Current time = 2021-09-16T16:11:28.121Z

我没想到会断开连接,因为显然没有任何连接问题。所以我的问题是,为什么在部署到 Cloud Run 时会出现这种断开连接?并且可以避免吗?

任何帮助表示赞赏,谢谢!

4

1 回答 1

2

根据评论,文档说:

WebSockets 请求在 Cloud Run 中被视为长时间运行的 HTTP 请求。即使您的应用程序服务器不强制任何超时,它们也会受到请求超时(目前最长 60 分钟,默认为 5 分钟)的影响。

因此,如果客户端保持连接打开的时间超过为 Cloud Run 服务配置的所需超时时间,则客户端将在请求超时时断开连接。

因此,如果请求超时或服务器断开连接,连接到 Cloud Run 的 WebSockets 客户端应处理重新连接到服务器。您可以通过使用 reconnecting-websocket 等库或在使用 SocketIO 库时处理“断开连接”事件在基于浏览器的客户端中实现此目的。

这将适用于基于 WebSockets 的 MQTT,因此更改超时将允许更长的连接,但您还应确保客户端在连接中断时自动重新连接。

于 2021-09-17T08:02:38.997 回答