Node之创建多进程应用程序

2020-01-06  本文已影响0人  27亿光年中的小小尘埃

在Node.js中,只使用一个线程来执行所有的操作。因此,如果在应用程序中存在某个操作需要大量消耗CPU资源的情况,则其他操作都会受到一定的影响。例如,当服务器正在执行一个非常消耗CPU资源的操作,则在该操作执行之后接收的客户端请求都需要等待该操作执行完毕后才能被处理。、

近些年来,服务器一般都开始使用多核CPU或者多CPU,许多服务器应用程序都开始依靠多线程或多进程机制来处理这些请求,以便可以更好地利用这些CPU资源。在Node.js中,同样提供一个child_process模块。通过该模块的使用,在Node.js应用程序的主进程运行之后,可以开启多个子进程。在多个子进程之间可以共享内存空间,可以通过子进程之间的互相通信来实现信息的交换,多个子进程之间也可以通过共享端口的方式将请求分配给多个子进程来执行。

使用spawn方法开启子进程

在child_process模块中,提供多个可以开启子进程的方法

首先,可以使用spawn方法开启一个用于运行某个命令的子进程。

child_process.spawn(command,[args],[options])

spawn方法返回一个隐式创建的代表子进程的ChildProcess对象。

spawn使用示例:

var cp=require('child_process');
var sp1 =cp.spawn('node',['test1.js','one','two','three','four'],{cwd:'./test'});
var sp2 =cp.spawn('node',['test2.js'],{stdio:'pipe'});
sp1.stdout.on('data',function (data) {
    console.log('子进程标准输出: '+data);
    sp2.stdin.write(data);
});
sp1.on('exit',function(code,signal) {
    console.log('子进程退出,退出代码为:'+code);
    process.exit();
});

父进程向子进程发送信号

在父进程中,可以使用子进程对象的kill方法向子进程发送信号。

child.kill([signal])

可以使用一个可选参数,参数值为用于描述该信号的字符串。默认参数值为’SIGTERM’(用于强制关闭进程)。

使用子进程的kill方法关闭子进程

var cp=require('child_process');
var sp1 =cp.spawn('node',['test1.js','one','two','three','four'],{cwd:'./test'});
var sp2 =cp.spawn('node',['test2.js'],{stdio:'pipe'});
sp1.stdout.on('data',function (data) {
    console.log('子进程标准输出: '+data);
    sp2.stdin.write(data);
    sp1.kill();
});
sp1.on('exit',function(code,signal) {
    if(!code)
    console.log('子进程退出,退出信号为:'+signal);
    else
    console.log('子进程退出,退出代码为:'+code);
    process.exit();
});
sp1.on('error',function (err) {
    console.log('子进程开启失败: '+err);
    process.exit();
});

如果利用spawn方法的options参数值对象的stdio属性中的'ipc'字符串值在父进程与子进程之间创建一个IPC通道,那么,当该通道关闭时,将触发子进程对象的disconnect事件。

var cp=require('child_process');
var sp1 =cp.spawn('node',['test1.js','one','two','three','four'],{cwd:'./test',stdio:['ipc','pipe','ignore']});
var sp2 =cp.spawn('node',['test2.js'],{stdio:['pipe']});
sp1.stdout.on('data',function (data) {
    console.log('子进程标准输出: '+data);
    sp2.stdin.write(data);
});
sp1.on('exit',function(code,signal) {
    console.log('子进程退出,退出代码为:'+code);
    process.exit();
});
sp1.on('error',function (err) {
    console.log('子进程开启失败: '+err);
    process.exit();
});
sp1.on('disconnect', function() {
    console.log('IPC通道被关闭。');
});

使用fork方法开启子进程

在child_process模块中,可以使用fork方法开启一个专用于运行Node.js中的某个模块文件的子进程。

child_process.fork(modulePath,[args],[options])

fork方法返回一个隐式创建的代表子进程的ChildProcess对象。

在使用fork方法时,当子进程中所有输入/输出操作执行完毕后,子进程不会自动退出。必须使用process.exit()方法将其显式退出。

当使用fork方法开启子进程后,可以使用子进程对象的send方法在父进程中向子进程发送消息,在子进程中也可以使用进程对象的send方法向父进程发送消息,

child.send(message,[sendHandle]) //
在父进程中向子进程发送消息
process.send(message,[sendHandle]) //
在父进程中向主进程发送消息

子父进程共享HTTP服务器

父进程

var http = require('http');
var child_process = require('child_process');
var fs = require('fs');
var child = child_process.fork('child.js');
var server = net.createServer();
server.listen(1337, '127.0.0.1', function () {
    child.send('server', server);
    console.log('父进程中的服务器已创建。');
    var httpServer = http.createServer();
    httpServer.on('request', function (req, res) {
        if(req.url!=="/favicon.ico"){
        var sum=0;
        for(var i=0;i<1000000;i++){
            sum+=i;
        }
        res.write("客户端请求在父进程中被处理。");
        res.end("sum="+sum);
    }
    });
    httpServer.listen(server);
});

子进程

var http = require('http');
process.on('message', function (msg,server) {
    if (msg === 'server') {
    console.log('子进程中的服务器已创建。');
    var httpServer = http.createServer();
    httpServer.on('request', function (req, res) {
        if(req.url!=="/favicon.ico"){
            sum=0;
            for(var i=0;i<1000000;i++){
            sum+=i;
        }
        res.write("客户端请求在子进程中被处理。");
        res.end("sum="+sum);
      }
    });
    httpServer.listen(server);
   }
});

连续向服务端发送10次请求

var http = require('http');
var options = {
    hostname: 'localhost',
    port: 1337,
    path: '/',
    method: 'GET'
};
for(var i=0;i<10;i++){
    var req = http.request(options,function(res) {
        res.on('data', function (chunk) {
            console.log('响应内容: '+chunk);
        });
    });
    req.end();
}

父进程与子进程共享socket端口对象

父进程

var child = require('child_process').fork('child.js');
var server = require('net').createServer();
server.on('connection', function(socket) {
    if (socket.remoteAddress!== '192.168.1.100') {
        child.send('socket', socket);
    return;
}
socket.end('客户端请求被父进程处理。');
});
server.listen(42367,'192.168.1.100');

子进程

process.on('message', function(m,socket) {
 if (m === 'socket') {
    socket.end('客户端请求被子进程处理。');
 }
});

使用exec方法开启子进程

在child_process模块中,可以使用exec方法开启一个用于运行某个命令的子进程并缓存子进程中的输出结果。

child_process.exec(command,[options],[callback])

exec方法中使用的callback参数用于指定子进程终止时调用的回调函数

function(error, stdout, stderr) {
    //回调函数代码略
}

exec方法与spawn方法的最大区别是,spawn方法可以在父进程中实时接收子进程中输出的标准输出流数据或标准错误输出流数据,因此是一个异步方法。如果使用exec方法,那么父进程必须等待子进程中的标准输出流数据或标准错误输出流数据全部缓存完毕后才能接收这些数据,因此是一个同步方法。

使用execFile方法开启子进程

在child_process模块中,可以使用execFile方法开启一个专用于运行某个可执行文件的子进程。

child_process.execFile(file,[args],[options],[callback])
上一篇下一篇

猜你喜欢

热点阅读