Process 与 多进程
多进程
为了解决同步架构的并发问题,一个简单的改进是通过进程的复制来服务跟多的请求。这样每一个链接都需要一个进程来服务,即100个连接需要启动100个进程来服务,这是非常昂贵的。在进程复制的过程中,需要复制进程的内部状态,对于每个连接都进行这样复制的话,相同的状态将会在内存中存在很多份,造成浪费。并且这个过程中由于复制较多的数据,启动是较为缓慢的。 为了解决启动缓慢的问题,预复制被引入到服务模型,即预先复制一定数量的进程。同时将进程进行复用,避免进程创建和销毁带来开销。
多线程
为了解决多进程复制过程中的浪费,多线程被引入,让一个线程服务一个请求。线程相对于进程开销要小很多,并且线程的数据是共享的,内存的浪费可以得到解决,并且利用线程池可以减小创建和销毁的开销。但是多线程锁面临的并发问题只能说比多进程略好,因为每个线程都拥有独立的堆栈,这个堆栈都需要占用一点的内存空间。另外一个CPU在同一时刻只能做一件事,操作系统只能通过将CPU切分为时间片的方式,让线程较为均匀的使用CPU资源,但操作系统内核在切换线程的同时也要切换线程的上下文,当线程数量过多时,时间将会消耗在上下文切换中。
事件驱动
事件驱动(event-driven)是nodejs中的第二大特性。何为事件驱动呢?简单来说,就是通过监听事件的状态变化来做出相应的操作。比如读取一个文件,文件读取完毕,或者文件读取错误,那么就触发对应的状态,然后调用对应的回掉函数来进行处理。
child_process
我们可以通过 child_process 这个模块来创建子进程。
- spawn:
启动一个子进程执行命令
- exec:
启动一个子进程执行命令,与 spawn 不同的是,具有一个回调函数获知子进程的状况
- execFile:
启动一个子进程执行可执行文件
- fork:
与 spawn 类似,需要指定一个 JavaScript 文件模块
Demo
// work.js
console.log('123');
// index.js
let cp = require('child_process');
cp.spawn('node',['work.js']);
cp.exec('node work.js',function(err,stdout,stdderr) {
})
// 这里的 work.js 需要加上 #!/usr/bin/env node
cp.execFile('work.js',function(err,stdout,stdderr) {
})
cp.fork('./work.js')
// exec execFile fork 的底层都是调用 spawn
进程间通讯
parent.js
:
let cp = require('child_process');
let child = cp.fork('./child.js');
child.on('message', function (message) {
console.log(`in parent,child say:${message}`);
});
child.send('parent:Do you have enough money?');
child.js
:
process.on('message', function (msg) {
console.log('in child,parent say:'+msg);
});
process.send('Yes, I do');
结果
:
in child,parent say:parent:Do you have enough money?
in parent,child say:Yes, I do
IPC
进程间通讯原理:通过 fork 或者其他 API 创建子进程,为了使父子进程进行通讯,父子进程之间将会创建IPC通道,通过IPC通道,父子进程通过send和message 进行通讯。 Linux 下 Domain Socket Windows 下 named pipe
进程间通讯技术
命名管道
匿名管道
socket
信号量:使用 kill -l 查看所有信号量
KILL 杀死/删除进程,编号为9
INT 中断信号,编号为2,当用户输入Ctrl+C时由终端驱动程序发送INT信号INT信号是终止当前操作的请求,简单程序捕获到INT信号时应该退出,拥有命令行或者输入模式的那些,程序应该停止他们正在做的事情,清除状态,并等待用户再次输入。
QUIT
USR1
共享内存
消息队列
Domain Socket
Cluster 模块
Cluster 原理,cluster 模块有 net 模块和 child_process.fork 实现,为何可以一个端口被多个进程监听,然后将master 进程创建的 fd 通过IPC传递个子进程。
//parent.js
var cp = require('child_process');
var child1 = cp.fork('./child.js');
var child2 = cp.fork('./child.js');
var server = require('net').createServer();
server.on('connection',function(socker) {
socker.end('handle by parent');
})
server.listen(1337,function() {
child1.send('server',server)
child2.send('server',server)
})
//child.js
process.on('message',function(m,server) {
if (m=='server'){
server.on('connection',function(socker) {
socker.on('handle by child!')
})
}
})