文章目录
- node 启动过程
- 单线程
- 多线程
- node 启动过程相关线程
- node 多线程特点
- 创建多线程
- 多进程
- 创建多进程
- cluster
- 进程守护
node 启动过程
- C++ 层引导:Node.js 启动时,会初始化 C++ 层的结构和依赖项,如 V8 引擎、Libuv 事件循环、核心模块等。此外,在这个阶段中,Node.js 也会解析命令行参数、环境变量,并初始化内存和全局对象
- V8 引擎初始化:Node.js 依赖于 V8 引擎来执行 JavaScript 代码。在启动过程中,Node.js 需要配置 V8 引擎并完成相应初始化。此外,Node.js 还会在这个阶段对 V8 引擎进行微调和优化,以获得更好的性能和运行效果。
- 内置模块加载:Node.js 内置了许多基本模块,如文件系统(fs)、网络(http)、事件(events)等。此外,Node.js 还会加载特定于平台的模块,例如 Windows、macOS 和 Linux 系统上各自的特定模块
- 执行入口文件
- 事件循环启动:所有初始化工作完成后,Node.js 将启动 Libuv 事件循环。
单线程
- node 虽然是单线程模型,但是其基于事件驱动、异步非阻塞 I/O 模型,可以应用于高并发场景,避免了线程创建、线程之间上下文切换所产生的资源开销。
- 但当项目中需要有大量计算 CPU 密集型的操作时,单线程模型就会被阻塞
多线程
node 启动过程相关线程
- node 单线程的指的是 JavaScript 的执行是单线程的,启动时会开启7个线程
- node 中最核心的是 v8 引擎,在 Node 启动后,会创建 v8 的实例,这个实例是多线程的。
-
主线程:编译、执行代码。
-
编译/优化线程:在主线程执行的时候,可以优化代码。
-
分析器线程:记录分析代码运行时间,为 Crankshaft 优化代码执行提供依据。node 有两个编译器,full-codegen:简单快地将 js 编译成简单但是很慢的机械码;Crankshaft:比较复杂的实时优化编译器,编译高性能的可执行代码。
-
垃圾回收的几个线程。
-
有一些 IO 操作(DNS,FS)和一些 CPU 密集计算(Zlib,Crypto)会启用 node 的线程池,从而将线程增加到11个(线程池默认大小为 4)
-
// 更改线程池默认大小
process.env.UV_THREADPOOL_SIZE = 12
node 多线程特点
- 每个线程都有单独的 V8 引擎
- ArrayBuffers 可以将内存中的变量从一个线程转到另外一个
- SharedArrayBuffer 可以在多个线程中共享内存中的变量,但是限制为二进制格式的数据。
创建多线程
worker
多进程
创建多进程
child_process.spawn():适用于返回大量数据,例如图像处理,二进制数据处理。
child_process.exec():适用于小量数据,maxBuffer 默认值为 200 * 1024 超出这个默认值将会导致程序崩溃,数据量过大可采用 spawn。
child_process.execFile():类似 child_process.exec(),区别是不能通过 shell 来执行,不支持像 I/O 重定向和文件查找这样的行为
child_process.fork(): 衍生新的进程,进程之间是相互独立的,每个进程都有自己的 V8 实例、内存,系统资源是有限的,不建议衍生太多的子进程出来,通长根据系统** CPU 核心数**设置。
cluster
- 实现原理
- cluster模块调用fork方法来创建子进程,该方法与child_process中的fork是同一个方法。
- 主从模型
- cluster 模块采用的是经典的主从模型,cluster会创建一个master,然后根据你指定的数量复制出多个子进程,可以使用 cluster.isMaster 属性判断当前进程是 master 还是 worker (工作进程)。
- 由 master 进程来管理所有的子进程,主进程不负责具体的任务处理,主要工作是负责调度和管理。
- cluster 模块使用内置的负载均衡来更好地处理线程之间的压力,该负载均衡使用了 Round-robin 算法(也被称之为循环算法)。当使用 Round-robin 调度策略时,master accepts() 所有传入的连接请求,然后将相应的 TCP 请求处理发送给选中的工作进程(该方式仍然通过IPC来进行通信)
- master 进程内部启动了一个 TCP 服务器,而真正监听端口的只有这个服务器,当来自前端的请求触发服务器的 connection 事件后,master 会将对应的 socket 句柄发送给子进程。
const cluster = require('cluster');
const http = require('http');
const numCPUs = require('os').cpus().length;
if (cluster.isMaster) {
console.log(`主进程 ${process.pid} 正在运行`);
for (let i = 0; i < numCPUs; i++) {
cluster.fork();
}
cluster.on('exit', (worker, code, signal) => {
console.log(`工作进程 ${worker.process.pid} 已退出`);
});
} else {
// 工作进程可以共享任何 TCP 连接。
// 在本例子中,共享的是 HTTP 服务器。
http.createServer((req, res) => {
res.writeHead(200);
res.end('Hello World');
}).listen(8000);
console.log(`工作进程 ${process.pid} 已启动`);
}
进程守护
- 当服务意外崩溃了是不能自动重启进程的,所以需要通过某些方式来守护这个开启的进程
- 第三方的进程守护框架,pm2 和 forever ,它们都可以实现进程守护,底层也都是通过 child_process 模块和 cluster 模块实现的