我有一个大问题的一个星期。我尝试转换我的node.JS项目实际上在单核上运行到多核与集群。
使用websockets,此时,我对事件没有任何问题,但是,对于xhr-polling或jsonp-polling,我在集群模式下遇到socket.io的大问题。
这是我的服务器配置:
00-generic.js
'use strict';
var http = require('http'),
os = require('os'),
cluster = require('cluster');
module.exports = function(done) {
var app = this.express,
port = process.env.PORT || 3000,
address = '0.0.0.0';
if(this.env == 'test'){
port = 3030;
}
var self = this;
var size = os.cpus().length;
if (cluster.isMaster) {
console.info('Creating HTTP server cluster with %d workers', size);
for (var i = 0; i < size; ++i) {
console.log('spawning worker process %d', (i + 1));
cluster.fork();
}
cluster.on('fork', function(worker) {
console.log('worker %s spawned', worker.id);
});
cluster.on('online', function(worker) {
console.log('worker %s online', worker.id);
});
cluster.on('listening', function(worker, addr) {
console.log('worker %s listening on %s:%d', worker.id, addr.address, addr.port);
});
cluster.on('disconnect', function(worker) {
console.log('worker %s disconnected', worker.id);
});
cluster.on('exit', function(worker, code, signal) {
console.log('worker %s died (%s)', worker.id, signal || code);
if (!worker.suicide) {
console.log('restarting worker');
cluster.fork();
}
});
} else {
http.createServer(app).listen(port, address, function() {
var addr = this.address();
console.log('listening on %s:%d', addr.address, addr.port);
self.server = this;
done();
});
}
};
03-socket.io.js
"use strict";
var _ = require('underscore'),
socketio = require('socket.io'),
locomotive = require('locomotive'),
RedisStore = require("socket.io/lib/stores/redis"),
redis = require("socket.io/node_modules/redis"),
v1 = require(__dirname + '/../app/socket.io/v1'),
sockets = require(__dirname + '/../../app/socket/socket'),
config = require(__dirname + '/../app/global'),
cluster = require('cluster');
module.exports = function () {
if (!cluster.isMaster) {
this.io = socketio.listen(this.server);
var pub = redis.createClient(),
sub = redis.createClient(),
client = redis.createClient();
this.io.enable('browser client minification'); // send minified client
this.io.enable('browser client etag'); // apply etag caching logic based on version number
this.io.enable('browser client gzip'); // gzip the file
this.io.set("store", new RedisStore({
redisPub : pub,
redisSub : sub,
redisClient : client
}));
this.io.set('log level', 2);
this.io.set('transports', [
'websocket',
'jsonp-polling'
]);
this.io.set('close timeout', 24*60*60);
this.io.set('heartbeat timeout', 24*60*60);
this.io.sockets.on('connection', function (socket) {
console.log('connected with ' + this.io.transports[socket.id].name);
// partie v1 @deprecated
v1.events(socket);
// partie v1.1 refaite
_.each(sockets['1.1'], function(Mod) {
var mod = new Mod();
mod.launch({
socket : socket,
io : this.io
});
}, this);
}.bind(this));
}
};
通过轮询,客户端会不时地在与启动的侦听器不同的进程上进行连接。同样,通信服务器向客户端发射。
通过一点点搜索,我发现有必要通过store.io的商店来共享数据连接。所以我构建了RedisStore socket.io,如文档中所示,但即便如此,我发现自己的事件没有安全到达,我仍然收到此错误消息:
warn: client not handshaken client should reconnect
修改
现在,没有调用警告错误。我将redisStore更改为socket.io-clusterhub但是现在,并不总是调用事件。有时,好像轮询请求被另一个工作者捕获而不是启动了听众,因此没有任何反应。这是新配置:
'use strict';
var http = require('http'),
locomotive = require('locomotive'),
os = require('os'),
cluster = require('cluster'),
config = require(__dirname + '/../app/global'),
_ = require('underscore'),
socketio = require('socket.io'),
v1 = require(__dirname + '/../app/socket.io/v1'),
sockets = require(__dirname + '/../../app/socket/socket');
module.exports = function(done) {
var app = this.express,
port = process.env.PORT || 3000,
address = '0.0.0.0';
if(this.env == 'test'){
port = 3030;
}
var self = this;
var size = os.cpus().length;
this.clusterStore = new (require('socket.io-clusterhub'));
if (cluster.isMaster) {
for (var i = 0; i < size; ++i) {
console.log('spawning worker process %d', (i + 1));
cluster.fork();
}
cluster.on('fork', function(worker) {
console.log('worker %s spawned', worker.id);
});
cluster.on('online', function(worker) {
console.log('worker %s online', worker.id);
});
cluster.on('listening', function(worker, addr) {
console.log('worker %s listening on %s:%d', worker.id, addr.address, addr.port);
});
cluster.on('disconnect', function(worker) {
console.log('worker %s disconnected', worker.id);
});
cluster.on('exit', function(worker, code, signal) {
console.log('worker %s died (%s)', worker.id, signal || code);
if (!worker.suicide) {
console.log('restarting worker');
cluster.fork();
}
});
} else {
var server = http.createServer(app);
this.io = socketio.listen(server);
this.io.configure(function() {
this.io.enable('browser client minification'); // send minified client
this.io.enable('browser client etag'); // apply etag caching logic based on version number
this.io.enable('browser client gzip'); // gzip the file
this.io.set('store', this.clusterStore);
this.io.set('log level', 2);
this.io.set('transports', [
'websocket',
'jsonp-polling'
]);
//this.io.set('close timeout', 24*60*60);
//this.io.set('heartbeat timeout', 24*60*60);
}.bind(this));
this.io.sockets.on('connection', function (socket) {
console.log('connected with ' + this.io.transports[socket.id].name);
console.log('connected to worker: ' + cluster.worker.id);
// partie v1 @deprecated
v1.events(socket);
// partie v1.1 refaite
_.each(sockets['1.1'], function(Mod) {
var mod = new Mod();
mod.launch({
socket : socket,
io : this.io
});
}, this);
}.bind(this));
server.listen(port, address, function() {
var addr = this.address();
console.log('listening on %s:%d', addr.address, addr.port);
self.server = this;
done();
});
}
};
答案 0 :(得分:2)
从该来源:http://socket.io/docs/using-multiple-nodes/
如果您计划分配不同的连接负载 进程或机器,您必须确保请求关联 具有特定会话ID连接到发起的进程 它们。
这是由于某些传输,如XHR轮询或JSONP轮询 依赖于在生命周期内发出几个请求 “插座”。
每次都要将连接路由到同一个工作人员:
<强>粘性会话强>
在socket.io文档中,这是每次都将请求路由到同一个worker的推荐方法。
https://github.com/indutny/sticky-session
将socket.io与群集一起使用的简单高效方法。
Socket.io正在执行多个请求来执行握手和 与客户建立联系。对于群集,这些请求可能会 到达不同的工作人员,这将打破握手协议。
var sticky = require('sticky-sesion');
sticky(function() {
// This code will be executed only in slave workers
var http = require('http'),
io = require('socket.io');
var server = http.createServer(function(req, res) {
// ....
});
io.listen(server);
return server;
}).listen(3000, function() {
console.log('server started on 3000 port');
});
在节点之间传递消息:
<强> socket.io-redis的强>
在socket.io文档中,这是在工作者之间共享消息的推荐方法。
https://github.com/automattic/socket.io-redis
通过使用socket.io-redis适配器运行socket.io,您可以运行 不同进程或服务器中的多个socket.io实例 所有人都可以相互广播和发送事件。
socket.io-redis以这种方式使用:
var io = require('socket.io')(3000);
var redis = require('socket.io-redis');
io.adapter(redis({ host: 'localhost', port: 6379 }));
同时强>
我认为你没有使用socket.io v1.0.0。您可能希望更新您的版本以获得更高的稳定性。
查看他们的迁移指南答案 1 :(得分:2)
使用
时,socket.io文档中缺少一个步骤var io = require('socket.io')(3000);
var redis = require('socket.io-redis');
io.adapter(redis({ host: 'localhost', port: 6379 }));
您需要告诉客户您要使用&#39; websockets&#39;作为唯一的传输形式或它不起作用...所以对于客户端的构造函数使用
io.connect(yourURL , { transports : ['websocket']});
在这里看到我对类似问题的回答(我的回答可能更适合这个帖子): https://stackoverflow.com/a/30791006/4127352
答案 2 :(得分:1)
以下代码对我来说,这是socket.io谁创建了集群,我在config.clusterSticky
上设置true
以激活兼容性集群和socket.io
'use strict';
/*
var cl = console.log;
console.log = function(){
console.trace();
cl.apply(console,arguments);
};
*/
var cluster = require('cluster'),
config = require('./config/all'),
deferred = require('q').defer(),
express = require('express'),
app = express(),
http = require('http'),
sticky = require('socketio-sticky-session'),
io = require('socket.io');
// Code to run if we're in the master process or if we are not in debug mode/ running tests
if ((cluster.isMaster) &&
(process.execArgv.indexOf('--debug') < 0) &&
(process.env.NODE_ENV !== 'test') && (process.env.NODE_ENV !== 'development') &&
(process.execArgv.indexOf('--singleProcess') < 0) &&
(!config.clusterSticky)) {
console.log('for real!');
// Count the machine's CPUs
var cpuCount = process.env.CPU_COUNT || require('os').cpus().length;
// Create a worker for each CPU
for (var i = 0; i < cpuCount; i += 1) {
console.log('forking ', i);
cluster.fork();
}
// Listen for dying workers
cluster.on('exit', function (worker) {
// Replace the dead worker, we're not sentimental
console.log('Worker ' + worker.id + ' died :(');
cluster.fork();
});
// Code to run if we're in a worker process
} else {
var port = config.http.port;
var workerId = 0;
if (!cluster.isMaster) {
workerId = cluster.worker.id;
}
var server = http.createServer(app);
io.listen(server);
//TODO routes etc (core)
server.on('listening', function () {
console.log('Slave app started on port ' + port + ' (' + process.env.NODE_ENV + ') cluster.worker.id:', workerId);
});
if(config.clusterSticky && (process.env.NODE_ENV !== 'test') && (process.env.NODE_ENV !== 'development')) {
sticky(server).listen(port);
} else {
server.listen(port);
}
deferred.resolve(server);
}
module.exports = deferred.promise;