我正在尝试使用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在关闭后停止接收消息,我该怎么做?