我有一个大问题 sice 1 周。我尝试将我在单核上实际运行的 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 for socket.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();
});
}
};
最佳答案
来自该来源:http://socket.io/docs/using-multiple-nodes/
If you plan to distribute the load of connections among different processes or machines, you have to make sure that requests associated with a particular session id connect to the process that originated them.
This is due to certain transports like XHR Polling or JSONP Polling relying on firing several requests during the lifetime of the “socket”.
每次都将连接路由到同一个工作人员:
粘性 session
在 socket.io 文档中,这是每次将请求路由到同一个工作人员的推荐方式。
https://github.com/indutny/sticky-session
A simple performant way to use socket.io with a cluster.
Socket.io is doing multiple requests to perform handshake and establish connection with a client. With a cluster those requests may arrive to different workers, which will break handshake protocol.
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');
});
在 Node 之间传递消息:
socket.io-redis
在 socket.io 文档中,这是在工作人员之间共享消息的推荐方式。
https://github.com/automattic/socket.io-redis
By running socket.io with the socket.io-redis adapter you can run multiple socket.io instances in different processes or servers that can all broadcast and emit events to and from each other.
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。您可能需要更新您的版本以获得更高的稳定性。
您可以在 http://socket.io/docs/migrating-from-0-9/ 查看他们的迁移指南
关于node.js - Socket.io、集群、快速和同步事件,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/23886054/
我在使用omniauth/openid时遇到了一些麻烦。在尝试进行身份验证时,我在日志中发现了这一点:OpenID::FetchingError:Errorfetchinghttps://www.google.com/accounts/o8/.well-known/host-meta?hd=profiles.google.com%2Fmy_username:undefinedmethod`io'fornil:NilClass重要的是undefinedmethodio'fornil:NilClass来自openid/fetchers.rb,在下面的代码片段中:moduleNetclass
这里有一个很好的答案解释了如何在Ruby中下载文件而不将其加载到内存中:https://stackoverflow.com/a/29743394/4852737require'open-uri'download=open('http://example.com/image.png')IO.copy_stream(download,'~/image.png')我如何验证下载文件的IO.copy_stream调用是否真的成功——这意味着下载的文件与我打算下载的文件完全相同,而不是下载一半的损坏文件?documentation说IO.copy_stream返回它复制的字节数,但是当我还没有下
我正在尝试解析一个文本文件,该文件每行包含可变数量的单词和数字,如下所示:foo4.500bar3.001.33foobar如何读取由空格而不是换行符分隔的文件?有什么方法可以设置File("file.txt").foreach方法以使用空格而不是换行符作为分隔符? 最佳答案 接受的答案将slurp文件,这可能是大文本文件的问题。更好的解决方案是IO.foreach.它是惯用的,将按字符流式传输文件:File.foreach(filename,""){|string|putsstring}包含“thisisanexample”结果的
是否有简单的方法来更改默认ISO格式(yyyy-mm-dd)的ActiveAdmin日期过滤器显示格式? 最佳答案 您可以像这样为日期选择器提供额外的选项,而不是覆盖js:=f.input:my_date,as::datepicker,datepicker_options:{dateFormat:"mm/dd/yy"} 关于ruby-on-rails-事件管理员日期过滤器日期格式自定义,我们在StackOverflow上找到一个类似的问题: https://s
1.错误信息:Errorresponsefromdaemon:Gethttps://registry-1.docker.io/v2/:net/http:requestcanceledwhilewaitingforconnection(Client.Timeoutexceededwhileawaitingheaders)或者:Errorresponsefromdaemon:Gethttps://registry-1.docker.io/v2/:net/http:TLShandshaketimeout2.报错原因:docker使用的镜像网址默认为国外,下载容易超时,需要修改成国内镜像地址(首先阿里
我正在尝试将以下SQL查询转换为ActiveRecord,它正在融化我的大脑。deletefromtablewhereid有什么想法吗?我想做的是限制表中的行数。所以,我想删除少于最近10个条目的所有内容。编辑:通过结合以下几个答案找到了解决方案。Temperature.where('id这给我留下了最新的10个条目。 最佳答案 从您的SQL来看,您似乎想要从表中删除前10条记录。我相信到目前为止的大多数答案都会如此。这里有两个额外的选择:基于MurifoX的版本:Table.where(:id=>Table.order(:id).
print"Enteryourpassword:"pass=STDIN.noecho(&:gets)puts"Yourpasswordis#{pass}!"输出:Enteryourpassword:input.rb:2:in`':undefinedmethod`noecho'for#>(NoMethodError) 最佳答案 一开始require'io/console'后来的Ruby1.9.3 关于ruby-为什么不能使用类IO的实例方法noecho?,我们在StackOverflow上
这是我在ActiveAdmin中的自定义页面ActiveAdmin.register_page"Settings"doaction_itemdolink_to('Importprojects','settings/importprojects')endcontentdopara"Text"endcontrollerdodefimportprojectssystem"rakedataspider:import_projects_ninja"para"OK"endendend我想做的是,当我单击“导入项目”按钮时,我想在Controller中执行rake任务。但是我无法访问该方法。可能是什
例如,假设我有一个名为Products的模型,并且在ProductsController中,我有以下代码用于product_listView以显示已排序的产品。@products=Product.order(params[:order_by])让我们想象一下,在product_listView中,用户可以使用下拉菜单按价格、评级、重量等进行排序。数据库中的产品不会经常更改。我很难理解的是,每次用户选择新的order_by过滤器时,rails是否必须查询,或者rails是否能够以某种方式缓存事件记录以在服务器端重新排序?有没有一种方法可以编写它,以便在用户排序时rails不会重新查询结果
我有一个将某些事件写入队列的Rails3应用。现在我想在服务器上创建一个服务,每x秒轮询一次队列,并按计划执行其他任务。除了创建ruby脚本并通过cron作业运行它之外,还有其他稳定的替代方案吗? 最佳答案 尽管启动基于Rails的持久任务是一种选择,但您可能希望查看更有序的系统,例如delayed_job或Starling管理您的工作量。我建议不要在cron中运行某些东西,因为启动整个Rails堆栈的开销可能很大。每隔几秒运行一次它是不切实际的,因为Rails上的启动时间通常为5-15秒,具体取决于您的硬件。不过,每天这样做几