最近感觉压力有点大,来看看nodejs的书,好让自己轻松一下。
正如标题所言,这篇文章写的是关于nodejs进程方面的知识。众所周知,nodejs是单线程的额,在编程方面有着得天独厚的先天优势,可以让我们省去了并发编程所带来的种种苦恼,基于事件驱动的它,面对着io高并发,可以做得很好,但是,有一点是出于劣势的--------面对cpu密集型的情况,可能会导致阻塞,还会浪费空余的cpu。是不是感到很可惜,美中不足啊。不怕不怕啦,多进程来啦!!!!
子进程间通讯
模块“child_process”可以给我们带来多线程。废话少说,代码最实际
//parent.js
var cp = require('child_process');
var n = cp.fork('./sub.js');
n.on('message', function(data) {
console.log(data.foo);
});
n.send({hello:'world'});
//sub.js
process.on('message', function(m) {
console.log(m.hello);
});
process.send({foo:'bar'});
有两个文件,parent.js是父进程,sub.js是被创建的子进程。意思很明显,子进程监听父进程发来的消息,同时,它也主动发消息给父进程。至于父进程的话,也是同一意图。猜猜以上会输出什么,貌似令人迷惑。迷惑就对了,事实上,我也迷惑。
---------------------------------集群之路-------------------------------------
下面我们可以来模仿一下集群。一个主进程的话,可以创建和管理众多的子进程。一个稳定的集群,当有任务来的时候,可以分发给自己的子进程去完成。当子进程有种种错误导致崩溃的时候,主进程可以让它重启。准确来说,是再生成另外一个进程。下面,上代码
//master.js
var fork = require('child_process').fork;
var cpus = require('os').cpus();
//创建服务器并监听
var server = require('net').createServer();
server.listen(41234);
var workers = {};
var createWorker = function() {
//创建子进程
var worker = fork('./workerall.js');
//退出时记得要重启
worker.on('exit', function() {
console.log('worker '+ worker.pid + ' exited');
delete workers[worker.pid];
createWorker(); //重启
});
worker.send('server', server);
workers[worker.pid];
console.log('create worker. pid: ' +worker.pid);
};
//新建好多个线程
for (var i =0; i<cpus.length; i++) {
createWorker();
}
//自己结束,子进程一起屎
process.on('exit', function(){
for(var pid in workers) {
workers[pid].kill();
}
});
上面的代码意思相当容易懂。首先是创建cpu核个子进程,让他们先工作起来。当子进程退出的时候,及时再生成一个子进程,保证系统的稳定。
下面是子进程代码
//workerall
var http = require('http');
//创建服务器
var server = http.createServer(function(req, res) {
res.writeHead(200);
res.end('handle by pid:' +process.pid);
});
var worker;
process.on('message', function(m, tcp) {
if(m==='server') {
worker = tcp;
worker.on('connection', function(socket) {
worker.emit('connect', socket);
});
}
});
//捕获错误
process.on('uncaughtException', function(err) {
//停止接受新的连接
worker.close(function() {
//所有已经断开了,是时候退出了
process.exit(1);
});
});
子进程一旦崩溃,立刻退出,父进程那边会监测到,然后再生成一个,保证系统的稳定。但是,我们可以发现上面代码有着一点小问题。在子进程是在完全退出之后才会让父进程另外再行动,这给我们带来一定的延迟效果,若所有子进程都是这样的话,到时候会没人工作了,所以,为了杜绝此情况,我们可以让子进程在准备退出之际,就生成另外一个,那要怎么做呢?很明显,可以采用消息通知。跟“老板”说清楚之后才辞职,不然突然离开会让人感觉很不好。于是乎,我们其中一段代码修改如下:
//子进程
//捕获错误
process.on('uncaughtException', function(err) {
process.send({act:'suicide'});
//停止接受新的连接
worker.close(function() {
//所有已经断开了,是时候退出了
process.exit(1);
});
});
代码process.send({act:'suicide'});发送消息给父进程。父进程相应代码修改如下:
var createWorker = function() {
//创建子进程
var worker = fork('./workerall.js');
//启动新的子进程
worker.on('message', function(msg) {
if(msg.act==='suicide') {
createWorker(); //重启
}
});
//退出时记得要重启
worker.on('exit', function() {
console.log('worker '+ worker.pid + ' exited');
delete workers[worker.pid];
});
worker.send('server', server);
workers[worker.pid];
console.log('create worker. pid: ' +worker.pid);
};
监控子进程发送的消息,若是将要“离开”了,就先“招聘”一个进程替代它。好了,事已至此,似乎已经比较稳定了。然而这里还有一个小小的问题。我是没有发现的,看了书才知道,哦,有这么一回事,真是活到老学到老。是这样样的,有可能我们的连接是长连接,不是HTTP的短链接。等待长连接断开可能需要较长的时间。为此,为已有的连接的断开设置一个超时时间是必要的,在限定的时间里强制退出。代码如下:
//捕获错误
process.on('uncaughtException', function(err) {
process.send({act:'suicide'});
//停止接受新的连接
worker.close(function() {
//所有已经断开了,是时候退出了
process.exit(1);
});
//设置超时(5秒)
setTimeout(function() {
process.exit();
}, 5000);
});
限定的时间是5秒钟,若不退出,则强制退出,不然浪费资源。
---------------------------------------------我是一个分割线,表打我------------------------------------
好了,今天到这里了,还有一点,Cluster,这个东东,官网有详细介绍,这里不多说了。有点累。
总结:
通过这个学习,我感觉自己的知识还是挺薄弱的。不过也好,学到不少东西。
node的单线程是相当薄弱的,既不能好好利用空余的cpu,又不足够稳定。集群就不一样了,通过简单的主从模式,可以提高不只是一个档次。每个进程有自己相应的责任,各自分工合作,并且做好,这样,一个个简简单单的东西,会变得好强大。
在现实中也一样,团队里面的人各司其职,并且认真投入,往往会有意想不到的效果。