Nodejs极简入门教程(三):进程
Node虽然自身存在多个线程,但是运行在v8上的JavaScript是单线程的。Node的child_process模块用于创建子进程,我们可以通过子进程充分利用CPU。范例:
varfork=require('child_process').fork;
//获取当前机器的CPU数量
varcpus=require('os').cpus();
for(vari=0;i<cpus.length;i++){
//生成新进程
fork('./worker.js');
}
这里了解一下包括fork在内的几个进程创建方法:
1.spawn(command,[args],[options]),启动一个新进程来执行命令command,args为命令行参数
2.exec(command,[options],callback),启动一个新进程来执行命令command,callback用于在进程结束时获取标准输入、标准输出,以及错误信息
3.execFile(file,[args],[options],[callback]),启动一个新进程来执行可执行文件file,callback用于在进程结束时获取标准输入、标准输出,以及错误信息
4.fork(modulePath,[args],[options]),启动一个新进程来执行一个JavaScript文件模块,这时候创建的是Node子进程
Node进程间通信
父进程
//parent.js
varfork=require('child_process').fork;
//fork返回子进程对象n
varn=fork('./child.js');
//处理事件message
n.on('message',function(m){
//收到子进程发送的消息
console.log('gotmessage:'+m);
});
//向子进程发送消息
n.send({hello:'world'});
子进程
//child.js
//处理事件message
process.on('message',function(m){
console.log('gotmessage:'+m);
});
//process存在send方法,用于向父进程发送消息
process.send({foo:'bar'});
需要注意的是,这里的send方法是同步的,因此不建议用于发送大量的数据(可以使用pipe来代替,详细见:http://nodejs.org/api/all.html#child_process_child_process_spawn_command_args_options)。
特殊的情况,消息中cmd属性值包含NODE_前缀(例如:{cmd:‘NODE_foo'}消息),那么此消息不会被提交到message事件(而是internalMessage事件),它们被Node内部使用。
send方法的原型为:
send(message,[sendHandle])
这里,sendHandle(handle)可以被用于发送:
1.net.Native,原生的C++TCPsocket或者管道
2.net.Server,TCP服务器
3.net.Socket,TCPsocket
4.dgram.Native,原生的C++UDPsocket
5.dgram.Socket,UDPsocket
send发送sendHandle时实际上不是(也不能)直接发送JavaScript对象,而是发送文件描述符(最终以JSON字符串发送),其他进程能够通过这个文件描述符还原出对应对象。
现在看一个例子:
父进程
//parent.js
varfork=require('child_process').fork;
varn=fork('./child.js');
varserver=require('net').createServer();
server.listen(7000,function(){
//发送TCPserver到子进程
n.send('server',server);
}).on('connection',function(){
console.log('connection-parent');
});
子进程
process.on('message',function(m,h){
if(m==='server'){
h.on('connection',function(){
console.log('connection-child');
});
}
});
通过端口7000访问此程序,得到输出可能为connection–parent也可能得到输出connection–child。这里子进程和父进程同时监听了端口7000。通常来说,多个进程监听同一个端口会引起EADDRINUSE的异常,而此例的情况是,不同的两个进程使用了相同的文件描述符,且Node底层在监听端口时对socket设置了SO_REUSEADDR选项,这使得此socket可以在不同的进程间复用。在多个进程监听同一个端口时,同一时刻文件描述符只能被一个进程使用,这些进程对socket的使用是抢占式的。
cluster模块
在Node的v0.8新增了cluster模块,通过cluster模块能够轻松的在一台物理机器上构建一组监听相同端口的进程。范例:
varcluster=require('cluster');
varhttp=require('http');
varnumCPUs=require('os').cpus().length;
//检查进程是否是master进程
if(cluster.isMaster){
for(vari=0;i<numCPUs;++i)
//生成新的worker进程(只有master进程才可以调用)
cluster.fork();
cluster.on('exit',function(worker,code,signal){
console.log('worker'+worker.process.pid+'died');
});
}else{
http.createServer(function(req,res){
res.writeHead(200);
res.end('helloworld\n');
}).listen(8000);
}
我们在worker进程中调用listen方法,监听请求将会传递给master进程。如果master进程已经存在一个正在监听的server符合worker进程的要求,那么此server的handle将会传递给worker,如果不存在,master进程则会创建一个,然后将handle传递给worker进程。
更多详细的关于cluster的文档:http://www.nodejs.org/api/cluster.html