用cluster的master可以共享多个worker的全局数据吗?
发布于 2 个月前 作者 yakczh 433 次浏览 来自 问答

比如数据保存在一个全局变量 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 ++ 但是第二个浏览器刷页面,没有反应

5 回复

最简单的办法是使用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没有关系。

回到顶部