比如数据保存在一个全局变量 var message里 这样多个woker和 master都同时读写这个数据会不会出错呢? app.js
var cluster = require('cluster');
var io = require('socket.io')();
var numCPUs = require('os').cpus().length;
console.log(' numCPUs is ',numCPUs);
var connections=0;
if (cluster.isMaster) {
console.log("master start...");
// Fork workers.
for (var i = 0; i < numCPUs; i++) {
cluster.fork();
}
cluster.on('listening',function(worker,address){
console.log('[Master ]listening: worker ' + worker.process.pid +', Address: '+address.address+":"+address.port);
});
cluster.on('exit', function(worker, code, signal) {
console.log('[Master] worker exit ' + worker.process.pid + ' died');
});
} else if (cluster.isWorker) {
io.on('connection', function(socket) {
console.log('this is process',process.pid);
connections++;
console.log("client ++",connections);
socket.on('disconnect', function() {
connections--;
console.log("client --",connections);
});
socket.on('message',function (msg) {
console.log(msg);
});
});
io.listen(9000);
}
test.html
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>
</title>
<script src="/js/socket.io.js"></script>
</head>
<body>
<script type="text/javascript">
var socket = io.connect('ws://localhost:9000',{'reconnection':true});
socket.on('message', function(msg){
console.log(msg);
});
socket.emit('message',{foo:11,bar:22});
</script>
</body>
</html>
这样启动app.js ,用第一个浏览器刷页面,能看到 控制台显示 client ++ 但是第二个浏览器刷页面,没有反应
最简单的办法是使用Redis。所有的worker都 向 Redis 里 写与读 全局数据。Redis也支持事务。我公司的WEB集群目前就是这么做的。什么session与application级的数据都在Redis里。 在Github里,nodejs的Redis Client库很多。如果再配合上async/await,代码就会更漂亮了。
我之前遇到过类似的问题。我的推测是:你的第二个浏览器的web socket连接并没有被Socket.io成功地创建。根据Socket.IO的文档,在集群工作模式下,Socket.IO创建连接要求“黏性会话”。要不然,Socket.IO,在集群模式下,创建web socket连接有概率性的失败。为此,Socket.IO的作者还实现过一个“黏性会话”的模块在github上:https://github.com/wzrdtales/socket-io-sticky-session 除了TCP握手之外,Socket.IO为保证对IE 10以下浏览器的向下兼容性,Socket.IO自己也有一个握手操作(基于若干个HTTP请求)。虽然这个功能很强大,但是,也给我们带来了额外的限制。 我建议:你把这个黏性会话的功能先加上,再试。
@stuartZhang 继续测试了一下, 这个负载均衡只有大量请求下才会显示 一两个单个的请求始终是一个Worker来完成
测试代码
const cluster = require('cluster');
var totalReqs = 0;
if (cluster.isMaster) {
const numCPUs = require('os').cpus().length;
console.log(numCPUs);
var broadcast = function() {
Object.keys(cluster.workers).forEach(function(i) {
// console.log(cluster.workers[i].process.pid + ":" + totalReqs);
cluster.workers[i].send({total: totalReqs});
});
}
for (var i = 0; i < numCPUs; i++) {
var worker = cluster.fork();
worker.on('message', function(msg) {
totalReqs++;
broadcast();
// console.log(msg+ " master total is :"+totalReqs);
});
}
} else {
console.log(process.pid);
const http = require('http');
var localReq = 0;
http.createServer((req, res) => {
process.send(" worker " + process.pid + " local is " + localReq + " sum is " + totalReqs);
res.writeHead(200);
localReq++;
//console.log("localreq is "+localReq+" sum is "+totalReqs);
res.end("local is " + localReq + " total is " + totalReqs);
}).listen(9000);
process.on('message', function(msg) {
// console.log(msg.total);
totalReqs=msg.total;
});
}
ab -n 1000 -c 100 http://localhost:8000/
但是这样只有最后一个worker 会更新 totalReqs 其他的都显示是0 不知道是哪里的代码写错了
@stuartZhang 这个黏性会话是不是指 socket从连接到断开的整个过程中 就绑定某一个cpu核心 ?
@yakczh 这里的黏性会话指的是:同一个web client始终发请求给同一个web server实例(或 服务进程)。默认配置是:负载均衡器(也就是 你例子中的主进程)“随机转发”或“依次转发”请求给forked worker服务进程。这个和cpu没有关系。