Node.js集群(pm2) - 已关闭的WebSockets接收消息

时间:2017-06-28 04:31:17

标签: javascript node.js nginx websocket pm2

我正在尝试使用Node.js和WebSockets构建一个简单但可扩展的聊天应用。我在我的Node实例之间使用RabbitMQ作为消息代理。我使用pm2在Nginx后面进行节点聚类。

我遇到的问题是,有时已经关闭的WebSockets正在接收新消息而不是开放消息。

以下是我的WebSocket服务器的代码:

var url = require('url');
var WebSocket = require('ws');
var rabbit = require('amqplib').connect('amqp://localhost');
var express = require('express');
var http = require('http');

const app = express();
const server = http.createServer(app);

var port = process.env.PORT || 3000;

server.listen(port, function() {
    console.log('Express server listening on port ' + server.address().port);
});

const q = "messages";

const wss = new WebSocket.Server({ server });

wss.on('connection', function onConnection(ws) {

    console.log('got websocket connection');

    rabbit.then(function(conn) {
        ws.conn = conn;
        console.log('consumer connected to rabbitMQ');
        return conn.createChannel();
    }).then(function(ch) {
        console.log('consumer channel created');
        return ch.assertQueue(q).then(function(ok) {
            console.log('consumer asserted queue');
            return ch.consume(q, function(msg) {
                console.log('received message from rabbitMQ: ', msg.content.toString());
                if (ws.readyState === 1) {
                    if (msg && msg.content) 
                        ws.send(msg.content.toString());
                } else {
                    console.warn('Websocket is not open, state is ', ws.readyState);
                }
            }, { noAck: true });
        });
    }).catch(function handleConsumerRabbitErr(err) {
        console.warn('Handled rabbit consumer error:', err.stack);
    });

    ws.on('message', function onMessage(msg) {

        console.log('got websocket message');

        if (ws.conn) {
            ws.conn.createChannel().then(function(ch) {
                console.log('producer channel created');
                return ch.assertQueue(q).then(function(ok) {
                    console.log('sending ' + msg + ' to rabbitMQ');
                    return ch.sendToQueue(q, Buffer(msg));
                });
            }).catch(function handleProducerRabbitErr(err) {
                console.warn('Handled rabbit producer error:', err.stack);
            });
        }

    });

    ws.on('close', function onClose() {
        console.log('websocket closing');
    });
});

我的Nginx配置:

http {

    upstream nodejs {
        server localhost:3000;
    }

    map $http_upgrade $connection_upgrade  {
        default upgrade;
        ''  close;
    }

    server {
        listen 80;
        listen 443 ssl;
        server_name 192.168.45.45;
        root /vagrant/static;

        # Redirect HTTP to HTTPS
        if ($scheme = http) {
            return 301 https://$server_name$request_uri;
        }

        proxy_http_version 1.1;
        proxy_set_header Host $host;
        proxy_set_header Connection "";
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;

        ssl_certificate /etc/ssl/certs/nginx-selfsigned.crt;
        ssl_certificate_key /etc/ssl/private/nginx-selfsigned.key;
        ssl_session_cache shared:SSL:1m;
        ssl_prefer_server_ciphers on;

        location /ws/ {
            proxy_set_header Upgrade $http_upgrade;
            proxy_set_header Connection $connection_upgrade;
            proxy_pass http://nodejs;
        }

        location /app/ {
            proxy_pass http://nodejs;
        }
    }
}

我使用以下命令运行应用程序(在我的VM上创建2个节点):

$ pm2 app.js -i 0 --name chat

以下是发送WebSocket消息,刷新页面以及发送另外两封消息后pm2 log的输出:

[STREAMING] Now streaming realtime logs for [all] processes
0|chat     | got websocket connection                # Initial websocket connection on node 0
0|chat     | consumer connected to rabbitMQ
0|chat     | consumer channel created
0|chat     | consumer asserted queue
0|chat     | got websocket message
0|chat     | producer channel created
0|chat     | sending test to rabbitMQ
0|chat     | received message from rabbitMQ:  test   # Websocket received message from RabbitMQ, as expected
0|chat     | websocket closing
1|chat     | got websocket connection                # Page refresh, new websocket connection on node 1
1|chat     | consumer connected to rabbitMQ
1|chat     | consumer channel created
1|chat     | consumer asserted queue
1|chat     | got websocket message
1|chat     | producer channel created
1|chat     | sending test to rabbitMQ
0|chat     | received message from rabbitMQ:  test   # ERROR - old websocket received message from RabbitMQ!
0|chat     | Websocket is not open, state is  3      # Note these two lines are from node 0
1|chat     | got websocket message                   # Sent another message without refreshing the page
1|chat     | producer channel created
1|chat     | sending test to rabbitMQ                # This one succeeds, and it alternates from here
1|chat     | received message from rabbitMQ:  test

我发现它在发送的每个第n条消息上都成功,其中n是我打开的网页数量(也就是我刷新网页的次数),无论是腹板是否已关闭。

我的期望是,在关闭WebSocket之后,它的message事件不应该再发生了。显然,情况并非如此。

如何确保WebSocket在关闭后停止接收消息,我该怎么做?

1 个答案:

答案 0 :(得分:0)

如果我有RTFM for RabbitMQ,我会知道默认情况下从队列中消费是循环的。事实上,我所寻找的是pub/sub with exchanges