node虽然是单进程单线程运行的,但是也提供创建子进程让程序运行的更加快速,稳健。创建方法如下:
spawn: 用法:spawn(command[, args][, options])
,具体的参数请看官网。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 // app.js const { spawn } = require('child_process' ); var sp1 = spawn('node' , ['test.js' , 'one' , 'two' ,'three' ],{stdio:['pipe' ,'ipc' , 'ignore' ]}); var sp2 = spawn('node' ,['test1.js' ],{stdio:['pipe' ]}); sp1.on('message' , function (data){ console.log('child out:' +data); sp2.stdin.write(data); }); sp1.on('exit' ,function (code, signal){ console.log('exit:' +code); process.exit (); }); sp1.on('error' , function (err){ console.log('error:' +err); process.exit (); }); sp1.on('disconnect' ,function (){ console.log('ipc closed' ); });
1 2 3 4 5 // test.js process.send('cwd:' + process.cwd()); process.argv.forEach (function (val, index ){ process.send('\r\n' +index +':' +val); })
1 2 3 4 5 6 7 8 9 10 // test1.js var fs = require('fs' ); var out = fs.createWriteStream('./space.txt' ); process.stdin.on('data' ,function (data){ out.write(data); }); process.stdin.on('end' ,function (){ process.exit (); })
这时直接运行:
发现test.js 中的data还没发送完成到父进程,程序就exit 了,查看官网exit事件,有一句解释 当 ‘exit’ 事件被触发时,子进程的 stdio 流可能依然是打开的 。也就是说,子进程一旦结束就触发exit事件,但是数据有可能还没传输完成,这样就解释的通了, 我们注释掉exit事件,再看看运行结果:
这样就运行正确了。
fork: 用法:fork(modulePath[, args][, options])
[options]silent
: false继承父进程的stdio,设置为true,子进程的stdin、stdout和stderr都会通过管道传递到父进程,否则他们将会从父进程继承。[options]stdio
:[0,1,2,’ipc’], 如果要设置该属性值,则必须有个值为‘ipc’,否则报错。 该方法默认建立IPC通道
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 const { fork } = require ('child_process' );var sp1 = fork('test.js' , ['one' , 'two' ,'three' ]);var sp2 = fork('test1.js' ,{silent : true }); sp1.on('message' , function (data ) { console .log('child out:' +data); sp2.send(data); }); sp2.stdout.on('data' ,function (data ) { console .log(data.toString() + '\r\n' ); }); sp1.on('error' , function (err ) { console .log('error:' +err); process.exit(); }); sp1.on('disconnect' ,function ( ) { console .log('ipc closed' ); });
1 2 3 4 5 6 // test.js process.send('cwd:' + process.cwd()); process.argv.forEach (function (val, index ){ console.log('44444' ); process.send('\r\n' +index +':' +val); })
1 2 3 4 5 6 7 8 9 var fs = require ('fs' );var out = fs.createWriteStream('./space.txt' );var i = 0 ; process.on ('message' ,function(data ){ console.log ('xxx' + i); i++; out.write(data ); });
运行结果如下:
可以看到silent:false
时,和父进程共享stdio,silent为true时,子进程的stdio事件被传递到父进程上,通过监听stdio上的data事件可获取子进程中的输出数据。输出的结果杂乱无序,可以看出多进程是各自运行的。
exec: 用法:exec(command[, options][, callback])
exec
通过命令生成一个shell来运行相关的子进程。和spawn
的区别是exec
子进程返回的stdio
数据是同步的,而spawn
是异步的。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 // app.js const { exec } = require('child_process' ); var sp1 = exec('node test.js one two three' ,function (err, stdout, stderr){ if (err) { console.log('error:' + err); process.exit (); }else { console.log('child output:' +stdout.toString()); sp2.stdin.write(stdout.toString()); } }); var sp2 = exec('node test1.js' ,function (err, stdout, stderr){ process.exit (); });
1 2 3 4 5 // test.js process.stdout .write ('cwd:' + process.cwd()); process.argv.forEach(function (val, index) { process.stdout .write ('\r\n' +index+':' +val); })
1 2 3 4 5 6 7 // test1.js var fs = require('fs' ); var out = fs.createWriteStream('./space.txt' ); process.stdin.on('data' ,function (data){ out.write(data); process.exit (); });
运行结果:
execFile: 用法:execFile(file[, args][, options][, callback])
file参数是可执行的文件,在windows中如.bat文件。 和exec的区别是execFile直接通过可执行文件来生成子进程,相比exec稍微高效点。
翻看源码exec是对execFile的封装,而execFile和fork底层都是调用的spawn。
cluster: node提供cluster模块充分利用多核cpu对子进程处理。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 var cluster = require ('cluster' );var http = require ('http' );const numCPUs = require ('os' ).cpus().length;if (cluster.isMaster){ for (var i = 0 ; i < numCPUs; i++) { cluster.fork(); } console .log('run in main process' ); } else { http.createServer(function (req, res ) { if (req.url !== '/favicon.ico' ) { res.writeHead(200 , {'Content-Type' : 'text/html' }); res.write('<head><meta charset="utf-8"/></head>' ); res.end('hello\n' ); console .log('run in child process' ); } }).listen(1337 ); }
运行node app, 先输出 run in main process, 然后打开浏览器localhost:1337,则会发现输出 run in child process.
对于操作系统中其实是不允许多个进程对同一个端口进行监听的,cluster生成子进程其实是利用的child_process.fork()
,实现的原理其实是 利用主进程process.send方法中的第二个参数process.send(message [, sendHandle])将服务器对象或者socket对象传输给子进程,从而创建的子进程们都监听同一个socket端口对象。