进程
进程Process是计算机资源分配的最小单元,node中通过 node app.js 开启一个进程,多进程就是进程的复制(fork),fork 出来的每个进程都拥有自己的独立空间地址、数据栈。
- node中开启一个进程的例子
const http = require('http');
const server = http.createServer();
server.listen(3000,()=>{
process.title='我是一个进程';
console.log('进程id',process.pid)
})
然后系统中就会多出一个正在运行的进程。
线程
线程thread是计算机能进行运算的最小单元。线程隶属于进程,一个线程只能隶属于一个进程,但是一个进程是可以拥有多个线程的。
JS就是典型的单线程语言,因为对DOM的操作决定JS必须是单线程的,但是有些JS中的操作特别费时,比如ajax,此时过多的此类操作会造成线程堵塞,因此此类操作多利用JS的异步操作。
计算造成线程堵塞的例子
const http = require('http');
const longComputation = () => {
let sum = 0;
for (let i = 0; i < 1e10; i++) {
sum += i;
};
return sum;
};
const server = http.createServer();
server.on('request', (req, res) => {
if (req.url === '/compute') {
console.info('计算开始',new Date());
const sum = longComputation();
console.info('计算结束',new Date());
return res.end(`Sum is ${sum}`);
} else {
res.end('Ok')
}
});
server.listen(3000);
//打印结果
//计算开始 2019-07-28T07:08:49.849Z
//计算结束 2019-07-28T07:09:04.522Z
单线程的一些说明
- Node.js 虽然是单线程模型,但是其基于事件驱动、异步非阻塞模式,可以应用于高并发场景,避免了线程创建、线程之间上下文切换所产生的资源开销。
- 当你的项目中需要有大量计算,CPU 耗时的操作时候,要注意考虑开启多进程来完成了。
- Node.js 开发过程中,错误会引起整个应用退出,应用的健壮性值得考验,尤其是错误的异常抛出,以及进程守护是必须要做的。
- 单线程无法利用多核CPU,但是后来Node.js 提供的API以及一些第三方工具相应都得到了解决,文章后面都会讲到。
Node.js 中的进程与线程
Node.js是JS运行在服务端的运行环境,构建在Chrome的V8引擎上,基于事件驱动、非阻塞I/O模型,适合于 I/O 密集型的应用场景,Web业务开发中,如果你有高并发应用场景那么 Node.js 会是你不错的选择。。
在多核 CPU 系统之上,可以通过 child_process.fork 开启多个进程,即多进程+单线程模式,开启多进程不是为了解决高并发,主要是解决了单进程模式下 Node.js CPU 利用率不足的情况,充分利用多核 CPU 的性能。
Node.js中的进程
Node.js 中的进程 Process 是一个全局对象,无需 require 直接使用,给我们提供了当前进程中的相关信息。官方文档提供了详细的说明,感兴趣的可以亲自实践下 Process 文档。
此外,Node.js中存在child_process 模块与cluster 模块用于开启子进程(fork),核心就是父进程(即 master 进程)负责监听端口,接收到新的请求后将其分发给下面的 worker 进程。
Node.js进程守护
每次启动 Node.js 程序都需要在命令窗口输入命令 node app.js 才能启动,但如果把命令窗口关闭则Node.js 程序服务就会立刻断掉。除此之外,当我们这个 Node.js 服务意外崩溃了就不能自动重启进程了。这些现象都不是我们想要看到的,因此一些第三方的进程守护框架,pm2 和 forever ,它们都可以实现进程守护,底层也都是通过上面讲的 child_process 模块和 cluster 模块实现的。
Node.js中的线程
在启动Node进程时,会开启7个线程而不是只有1个,不是说好的每个进程对应一个线程吗?这是因为Node 中最核心的是 v8 引擎,在 Node 启动后,会创建 v8 的实例,这个实例是多线程的:
- 主线程:编译、执行代码
- 编译/优化线程:优化代码
- 分析器线程:记录分析代码运行时间,为 Crankshaft 优化代码执行提供依据
- 垃圾回收的几个线程
所以大家常说的 Node 是单线程的指的是 JavaScript 的执行是单线程的(开发者编写的代码运行在单线程环境中),但 Javascript 的宿主环境,无论是 Node 还是浏览器都是多线程的。因为libuv中有线程池的概念存在的,libuv会通过类似线程池的实现来模拟不同操作系统的异步调用,这对开发者来说是不可见的。
在Node10.5.0发布后,官方才给出了一个实质性的多线程模块 worker_threads,给Node提供了多线程的能力。