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