Nodejs有多重并发能力
- 单线程异步
- 多线程
- 多进程
非阻塞异步IO
单线程,不利于执行CPU密集型任务
Nodejs 的IO是非阻塞的,发出IO后交给系统线程池去执行IO,Nodejs继续执行其他代码,所以能够实现非阻塞的异步IO
当IO完成后,通过事件机制通知到时间循环的 Poll 阶段去执行回调 (nodejs通过libuv获取系统通知)
所以使用Nodejs执行并发IO的操作的时候我们应该考虑的是下游的承载能力,通过并发池来控制并发数
const fs = require('fs');
function readFileAsync() {
fs.readFile('file.txt', 'utf8', (err, data) => {
if (err) throw err;
console.log(data);
});
}
// 可以在读取文件的同时执行其他操作
readFileAsync();
console.log('继续执行其他任务');
子进程 Child Process
多进程(cluster & child process) 必须使用IPC通信
而且创建进程也会有性能损耗
子进程可以利用其他CPU核心
const { fork } = require('child_process');const child = fork('./child.js');
child.send('start');
child.on('message', (msg) => {console.log('Received from child:', msg);});
多进程 Cluster
计算机资源调度和分配的基本单位,无法共享内容,需要通过消息通信
const cluster = require('cluster');
const http = require('http');
if (cluster.isMaster) {
console.log(`Master ${process.pid} is running`);
// Fork workers
for (let i = 0; i < 4; i++) {
cluster.fork();
}
cluster.on('exit', (worker, code, signal) => {
console.log(`Worker ${worker.process.pid} died`);
});
} else {
console.log(`Worker ${process.pid} is running`);
http.createServer((req, res) => {
res.writeHead(200, { 'Content-Type': 'text/plain' });
res.end('Hello, World!\n');
}).listen(3000);
}
由于多进程是独立的内存环境,所以还能够防止整个程序崩溃
多线程 Worker Threads
进程中的一个执行单元,可以共享进程内的内存
直接通过原子操作共享内存,需要注意线程安全问题
const { Worker, isMainThread, parentPort } = require('worker_threads');
if (isMainThread) {
const worker = new Worker(__filename);
worker.on('message', (message) => {
console.log('Received from worker:', message);
});
worker.postMessage('Hello, worker!');
} else {
parentPort.on('message', (message) => {
console.log('Received from main:', message);
parentPort.postMessage('Hello, main thread!');
});
}
aysnc_hook 异步追踪
- createHook 方法
较底层的方法,为每个异步任务配置回调钩子
- AsyncLocalStorage 方法
利用TLS(threal local storage)的思想在异步任务维度做本地存储以解决闭包问题
- AsyncResource 类 asyncLoaclStorage & AsyncResource.runInAsyncScope 方法
利用runInAsyncScope 复现异步当前类的Callback环境
runInAsyncScope是同步的,其中的异步函数也能够获得正确store数据
CommonJS & ES Module
- CommonJS (cjs)
- 同步执行,导出拷贝
- 随处可用
- 循环引用会导致依赖不可靠
- exports module.exports require
- ES Module (mjs)
-
异步执行,导出引用
-
在文件顶部使用
-
静态扫描提前发现循环应用问题
-
export export default import
多进程和多线程
JS代码的执行是单线程异步非阻滞的
- child_process 子进程
执行命令/调用子进程
spawn exec execFile fork ****(创建nodejs子进程)
- cluster 集群多进程
相比fork是更加高级和抽象的封装
通过IPC通信复用主进程HTTP端口服务
内置简单的负载均衡
- worker_threads 多线程
BroadcastChannle 多线程共享信息
可以通过序列化消息通信或者内存共享(传引用)
流和网络
Stream : 可读流可写流双工流
Protocol: TCP , UDP , HTTP , SOCKET ...
异步策略收益
-
剥离长耗时,重资源任务,降低请求延迟
-
缓冲大量突发性请求,削峰
-
节约成本
-
有利于完善重试和错误处理
-
将异步任务并行执行,提高速度
-
更好的任务优先级管控和流控
-
多样的任务触发方式
-
更好的观测性,异步任务有利于提供任务日志,指标,状态查询等
-
更高的研发效率,专注于处理逻辑的实现(Serverless思想)
Node Inspector
作用: 调试nodejs代码,收集Nodejs进程的Heap,Snapshot,CPU Profile等
协程coroutine与纤程fiber
协程和纤程都是线程下的更小的单位,可以看作是用户态的线程
- 线程的调度由操作系统调度,是抢占式的
- 协程和纤程的调度有代码显示控制,再JS中生成器就是一个典型的利用了协程实现的异步模型,async await 其实就是利用生成器创建的语法糖
笔者才疏学浅,请多多指教。 部分插图来自网络,侵删。