单个node实例运行在单线程中,要发挥多核系统的能力,用户有时候需要启动一个node进程集群来处理负载。
集群(cluseter)模块,允许你方便的创建一个共享服务器端口的进程网络。
代码如下:
var http = require("http");
var cluster = require('cluster');
var os = require('os');
var numCPUs = os.cpus().length;// 获取CPU 的数量,其实是核的个数,因为好多cpu都是多核的。
var workers = {};//一个存储活动工作进程对象的哈希表,以id字段为主键。它能被用作遍历所有工作进程,仅在主进程中可用
// 遍历所有工作进程
function eachWorker(callback) {
for (var id in cluster.workers) {
callback(cluster.workers[id]);
}
}
eachWorker(function(worker) {
worker.send('向一线工作者们致以亲切问候!');
});
if (cluster.isMaster){//如果是主进程则啥都不干,只负责创建新的进程(数量与cpu数量一样)
cluster.on('death', function (worker) {//注册集群的死亡事件,当一个进程死亡的时候调用[删除死亡进程,并重新fork一个添加到队列]
// 当一个工作进程结束时,重启工作进程
console.log("one workThread death ,restart another");
delete workers[worker.pid];
worker = cluster.fork();//返回子进程的process对象需要使用:cluster.fork().process
workers[worker.pid] = worker;
}
);
// 初始开启与CPU 数量相同的工作进程
for (var i = 0; i < numCPUs; i++) {
var worker = cluster.fork();//创建worker进程,返回子进程的process对象需要使用:cluster.fork().process
workers[worker.pid] = worker;
}
}else if(cluster.isWorker){//如果不是主进程,则进行真正的业务处理【启动服务器,并返回结果】
// 工作进程分支,启动服务器
console.log("[worker] start worker ..." + cluster.worker.id);
var app = http.createServer(function (req, res) {
console.log('worker'+cluster.worker.id);//cluster.worker获得当前的worker对象
res.end('worker'+cluster.worker.id+',PID:'+process.pid+" ok");
}).listen(3000);
}
// 当主进程被终止时,关闭所有工作进程
process.on('SIGTERM', function () {
console.log('mainThread sigterm!!');
for (var pid in workers) {
process.kill(pid);
}
process.exit(0);}
);
将上面的代码保存为cluster.js文件,然后在node环境去执行: node cluster.js
结果发现:windows不能生效
而linux可以顺利执行
后来在node的开发api中查询到原因,地址是:http://nodeapi.ucdok.com/#/api/cluster.html
-==============================================================
继续来点后话:在后续的开发中遇到点问题:
我们linux服务器是双cpu四核,所以一共能起8个进程,也就是上面的os.cpus().length
我的需求是要接受请求,进而来收集请求的数据写文件:
考虑到多线程,所以我就想着这样的话好需要自己来控制,每个进程(也就每个核)要独立的去写属于自己核操作的文件:
于是就自己为每个核去写计数器什么的,很辛苦的,结果。。。。结果。。。。结果。。。。结果是:不需要自己写
node的这种机制本身就是单线程,每个请求接受的处理都是独立的。
代码如下:
//*********************************************************
var http = require("http");
var url=require("url");
var querystring=require("querystring");
var cluster = require('cluster');
var os = require('os');
var fs = require('fs');
var numCPUs = os.cpus().length;// 获取CPU 的数量
var workers = {};
console.log('numCPUs=' + numCPUs);
console.log('cluster.isMaster=' + cluster.isMaster);
if (cluster.isMaster){
console.log('mainThread!');
// 主进程分支
cluster.on('death', function (worker) {
// 当一个工作进程结束时,重启工作进程
console.log("one workThread death ,restart another");
delete workers[worker.pid];
worker = cluster.fork();//返回子进程的process对象需要使用:cluster.fork().process
workers[worker.pid] = worker;
}
);
// 初始开启与CPU 数量相同的工作进程
for (var i = 0; i < numCPUs; i++) {
var worker = cluster.fork();//创建worker进程,返回子进程的process对象需要使用:cluster.fork().process
workers[worker.pid] = worker;
}
}else if(cluster.isWorker){
var core_counters = 0;//先定义每个core接受的请求的多个计数器
var core_counters_all = 0;//先定义每个core接受的请求总共请求个数的计数器
var files = '';//每个core的文件内容
var fileNums = 0;//每个核心写文件的个数
console.log("cluster.worker.id = "+cluster.worker.id);
// 工作进程分支,启动服务器
//http://localhost:3000?appId=736963&userId=1349387&funId=1001&funNum=2345&clientType=Firefox&clientVersion=3.20.12
http.createServer(function(request,response){
console.log("Request received. "+'worker'+cluster.worker.id);//cluster.worker获得当前的worker对象 ,从1开始计数
var pathname = url.parse(request.url).pathname; //pathname => select
var arg = url.parse(request.url).query; //arg => name=a&id=5
var appId = querystring.parse(arg).appId;
var userId = querystring.parse(arg).userId;
var funId = querystring.parse(arg).funId;
var funNum = querystring.parse(arg).funNum;
var clientType = querystring.parse(arg).clientType; //clientType => Firefox 浏览器类型
var clientVersion = querystring.parse(arg).clientVersion; //clientVersion => 10.23.5 浏览器版本
core_counters++;
core_counters_all++;
files += appId+"\t"+userId+"\t"+funId+"\t"+funNum+"\t"+clientType+"\t"+clientVersion+"\n";
console.log(core_counters);
console.log(core_counters_all);
if(core_counters>0&&(core_counters%100==0)){//每有100个请求,准备到内存中去写一次 //如果还没有攒够,就先在内存中加
console.log("ready to write!!!");
response.writeHead(200, {"Content-Type": "text/plain"});
var fileName = "core"+(parseInt(cluster.worker.id))+"_"+fileNums+".text";
if(core_counters_all<300){//如果现有文件,没有超过300条则在旧文件基础上追加
console.log("append to file !!!!!!!!!!!!!!!!!!!");
fs.appendFile(fileName,files,function(err){
if(err){
return;
}
});
}else{//如果现有文件已经达到在新写入一个文件
fileNums++;
core_counters= 0;
core_counters_all = 0;
files = "";//清空缓存内容
}
}
response.end();
console.log("request end.. ");
}).listen(3000);
}
// 当主进程被终止时,关闭所有工作进程
process.on('SIGTERM', function () {
console.log('mainThread sigterm!!');
for (var pid in workers) {
process.kill(pid);
}
process.exit(0);}
);
//**********************************************************************************
大家看看执行结果就知道了;
所以说不需要我们自己来考虑多进程的问题,就按照单进程来写代码就行,node cluster会自动进行负载均衡。