如何理解node的多进程
事件驱动
nodejs基于chrome v8引擎构建,默认单线程单进程模式,nodejs的单线程指的是js引擎只有一个实例,且是在主线程执行的(减少线程间切换的开销,不用考虑锁和线程池问题),其他的异步IO和事件驱动相关的线程通过libuv实现内部的线程池和线程调度。libuv存在一个事件循环,事件循环中维持一个执行栈和任务队列,在执行栈中,如果有异步IO及定时器等函数的话,就把异步回调函数放入事件队列中,执行栈执行完成后,从事件队列中按照一定的顺序执行事件队列中的异步回调。
模块调用
nodejs中的模块调用,js代码调用c++核心模块,核心模块调用内建模块,通过libuv进行系统调用,送入线程池等待执行。线程池中的I/O操作调用完成之后会保存结果向IOCP(一种高性能的I/O模型,一种应用程序使用线程池处理异步I/O请求的机制)提交执行状态告知当前对象操作完成并将线程归还线程池
非阻塞I/O
程序执行过程中需要进行很多I/O操作,读写文件、输入输出、请求响应等,I/O操作较为费时,基于node的事件循环机制,在I/O操作的同时,可以继续执行其他的代码。如文件IO,nodejs调用libuv后,libuv在其他线程执行I/O任务,线程完成任务后会通知主线程,主线程回调nodejs。
多进程
nodejs以单线程模式运行,使用事件驱动处理并发,可以在多核CPU系统上创建多个子线程。进程分为master进程和worker进程,master进程负责调度和管理worker进程,worker进程负责具体的业务处理,在服务器层面,worker是一个服务进程,负责处理来自客户端的请求,多个worker相当于多个服务器,因此构成一个服务器群,master进程负责创建worker,接收客户端的请求,分配到各服务器上去处理,监控worker的运行状态及管理操作。
node提供child_process模块创建子进程。nodejs实现多进程
- spawn 使用指定的命令行参数创建新进程
- fork 用于在子进程中运行的模块,基于
spawn
的封装,fork('./main.js)
相当于spawn('node', ['./main.js'])
,fork会在父进程与子进程之间,建立一个用于通信的管道,用于进程间的通信- exec 使用子进程执行命令,缓存子进程的输出,将子进程的输出以回调函数参数的形式返回。
execFile
基于spawn
封装,可以直接创建子进程进行文件操作,exec
基于execFile
封装,可以直接开启子进程执行命令,常见的应用场景如http-server以及webpack-dev-server命令行在启动本地服务是自动打开浏览器。
集群与进程通信
node主进程在创建子进程的时候吧把主进程正在监听的server当作参数传给子进程,子进程自己也创建一个server来监听主进程传来的server。通过process.on
中的message
接受信息,使用process.send
发送信息。
- ipc标准进程通信使用send方法发送消息时,第二个参数支持传入一个服务,必须是http服务或tcp服务,子进程通过message事件进行接收,回调的参数对应发送的参数,第一个参数为消息,第二个参数为服务,可以在子进程创建服务并对主进程的服务进行监听和操作
const os = require("os"); // os 模块用于获取系统信息
const http = require("http");
const path = require("path");
const { fork } = rquire("child_process");
// 创建服务
const server = createServer((res, req) => {
res.end("hello");
}).listen(3000);
// 根据 CPU 个数创建子进程
os.cpus().forEach(() => {
fork("child_server.js", {
cwd: path.join(__dirname);
}).send("server", server);
});
/**-------------------子进程业务的代码----------------------*/
const http = require("http");
// 接收来自主进程发来的服务
process.on("message", (data, server) => {
http.createServer((req, res) => {
res.end(`child${process.pid}`);
}).listen(server); // 子进程共用主进程的服务
});
- 单个nodejs实例运行在单个线程中,为了充分利用多核系统,有时需要启用一组nodejs进程去处理负载任务,cluster模块可以创建共享服务器端口的子进程。cluster模块,通过
isMaster
属性,判断是否是Master进程,是则fork
子进程,否则启动一个server
,每个HTTP Server都能监听到同一个端口。cluster
cluster 模块可以创建共享服务器端口的子进程。
const cluster = require("cluster");
const http = require("http");
const os = require("os");
// 判断当前执行的进程是否为主进程,为主进程则创建子进程,否则用子进程监听服务
if (cluster.isMaster) {
// 创建子进程
os.cpus().forEach(() => cluster.fork());
} else {
// 创建并监听服务
http.createServer((req, res) => {
res.end(`child${process.pid}`);
}).listen(3000);
}
模块分类
- 核心模块:包含在nodejs源码中,被编译进nodejs可执行二进制文件的js模块,是lib和deps目录下的js文件,如http/fs等
- 内建模块: 一般不直接调用,在native模块中调用,再require
-
第三方模块:非nodejs源码自带的模块为第三方模块,如webpack/express,如
lib
目录下的fs.js
是native
模块,而fs.js
调用的src
目录下的node_fs.cc
是内建模块