socket.io与pm2(cluster)集群搭配的解决方案

2020-06-17 06:39:15易采站长站整理
{"code":1,"message":"Session ID unknown"}
错误;即使前三次xhr握手成功,进入websocket连接升级阶段,负责侦听update事件的worker也往往不是之前的那个worder,因此导致websocket连接建立失败。

一言以蔽之,客户端多次请求的服务端进程不是同一个进程才导致的ws连接无法成功建立。那么如何才能解决呢?最简单的方案就是确保客户端的每次请求都可以定位到同一个服务进程即可。当然,分布式session同样可以解决问题,依托第三方缓存类似redis并配合一致性hash算法,确保所有服务进程都可以获取到连接信息,相互配合完成连接建立。但这也仅仅是作者在理论上分析的一种实现方式,并没有测试通过,因为这种分布式架构不仅实现繁杂而且引入了相关依赖redis,不太可取。

那么下文主要针对确保客户端的每次请求都可以定位到同一个服务进程这一点实现解决方案。

多种实现

官方实现

官方提供了一种比较轻便的架构:nginx反向代理+iphash

我们的示例demo中的http服务器只侦听8080端口,因此必须由pm2分发请求,否则会出现端口占用的错误发生。但是,官方的解决方案是每个进程的socket.io服务器创建不同端口的http服务器,专注用于http握手和升级,由nginx做握手请求的代理。而且针对nginx必须设置iphash,保证同一个客户端的多次请求定位到后端同一个服务进程。

这样,示例demo中会占用5个端口,其中8080端口为公用的http服务器使用,其他四个端口则只用于ws连接握手。但是这四个端口却如何选取呢?为了保证扩展性以及顺序性,采用与pm2相兼容的方案。pm2会为每个worker进程分配一个id,并且将该id绑定到进程的环境变量中,那么我们就可以利用该worker id生成4个不同的端口号。

app.js


var path = require('path');
var app = require('express')(),
server = require('http').createServer(app),
port = 3131 + parseInt(process.env.NODE_APP_INSTANCE),
io = require('socket.io')(port);

io
.on('connection', function(socket) {
socket.on('disconnect', function() {
console.log('/: disconnect-------->')
});

socket.on('b:message', function() {
socket.emit('s:message', '/: '+port);
console.log('/: '+port)
});
});

io.of('/ws')
.on('connection', function(socket) {
socket.on('disconnect', function() {
console.log('disconnect-------->')
});

socket.on('b:message', function() {
socket.emit('s:message', port);
});
});

app.get('/abc',function(req,res){
res.sendFile(path.join(process.cwd(),'./index.html'));
});

server.listen(8080);