首页IT科技nodejs支持多线程吗(Nodejs构建Cluster集群多线程Workerthreads)

nodejs支持多线程吗(Nodejs构建Cluster集群多线程Workerthreads)

时间2025-06-20 21:04:12分类IT科技浏览5068
导读:前言 前两天我们介绍了使用 Nodejs 中的 child_process 模块创建多个子进程,同时利用进程间通信的API构建了一个集群式的Web服务器。实际上,你可以通过 cluster 模块更方便的完成这一操作。...

前言

前两天我们介绍了使用 Nodejs 中的 child_process 模块创建多个子进程            ,同时利用进程间通信的API构建了一个集群式的Web服务器            。实际上                    ,你可以通过 cluster 模块更方便的完成这一操作                    。

但是        ,cluster 创建的进程之间无法共享内存         ,通信必须使用 JSON 格式                   ,有一定的局限性和性能问题        。如果你不想要进程隔离            ,可以使用 worker_thread 模块      ,它允许在一个 Node.js 实例中运行多个应用程序线程         。相比创建多个进程更轻量                  ,并且可以共享内存                   。

进程间通过传输 ArrayBuffer 实例或共享 SharedArrayBuffer 实例来做到这一点               ,对数据格式没有太多要求            。但是要注意   ,数据中不能包含函数      。

Cluster 多进程

我们可以使用 cluster 模块提供的API重构昨天的案例:

// master.js const cl = require("cluster"); const cpus = require("os").cpus().length; // 修改默认的 fork() 方法配置 cl.setupPrimary({ exec: worker.js }); for(let i = 0; i < cpus; i++) { cl.fork(); }; cl.on(listening, (data) => { console.log(`listenning on: ${data.id}--${data.process.pid}`); }); cl.on(exit, (data, code, signal) => { console.log(`exited: ${data.id}--${data.process.pid}, kill code: $[code], signal: ${signal}`); cl.fork(); });

子进程依旧使用昨天的代码:

const http = require("http"); const server = http.createServer((req, res) => { res.writeHead(200, { "Content-Type": "text/plain" }); res.end("Hello,World!" + process.pid); // 抛出异常                  ,捕获后终止进程 throw new Error(throw exception); }).listen(1337); // 捕获异常后终止进程 process.on(uncaughtException, (err) => { // 停止接收新的连接 server.close((data) => { console.log(`worker: ${process.pid} is stopping!`); process.exit(1); }) // 避免长连接请求长时间无法终止                  ,5s后自动终止 setTimeout(() => { process.exit(1); }, 5000) });

执行 node master.js,会得到与昨天利用 child_process 模块创建子进程集群相同的效果                  。

同样               ,你可以使用官方推荐的写法                     ,利用 cluster.isPrimary 和 cluster.isWorker 来判断当前进程是否为主进程:

const cluster = require(node:cluster); const http = require(node:http); const numCPUs = require(node:os).cpus().length; const process = require(node:process); if (cluster.isPrimary) { console.log(`Primary ${process.pid} is running`); // Fork workers. for (let i = 0; i < numCPUs; i++) { cluster.fork(); } cluster.on(exit, (worker, code, signal) => { console.log(`worker ${worker.process.pid} died`); }); } else { // Workers can share any TCP connection // In this case it is an HTTP server http.createServer((req, res) => { res.writeHead(200); res.end(hello world\n); }).listen(1337); console.log(`Worker ${process.pid} started`); };

实现原理

事实上    ,cluster 模块就是将 child_process 和 net 模块的API组合起来实现的               。cluster启动时            ,进程会在内部启动TCP服务器   。而在调用 cluster.fork() 复制子进程时                    ,会将这个TCP服务器端 Socket 的句柄发送给工作进程                  。如果进程是通过 cluster.fork() 复制出来的        ,那么它的环境变量里就存在 NODE_UNIQUE_ID                  。如果工作进程中存在 listen() 侦听网络端口的调用         ,它将拿到该句柄                   ,再通过 SO_REUSEADDR 端口重用            ,从而实现多个子进程共享端口。对于正常方式启动的进程      ,则不存在句柄共享和传递等过程               。

在 cluster 内部隐式创建TCP服务器的方式对使用者是透明的                  ,你不需要自己手动去实现句柄的传递               ,但也正是因此   ,它无法像使用 child_process 那样灵活                     。在 child_process 中你可以自行控制句柄的传送                  ,因此可以灵活地控制工作进程                  ,甚至控制多组工作进程    。

cluster事件

Event: disconnect 主进程和工作进程之间IPC通道断开后会触发该事件            。 Event: exit 有工作进程退出时触发该事件                    。 Event: fork 复制一个工作进程后触发该事件        。 Event: listening 工作进程中调用 listen() 后,发送该消息给主进程               ,主进程收到后                     ,触发该事件         。 Event: message Event: online fork好一个工作进程后    ,工作进程主动发送该消息给主进程            ,主进程收到消息后                    ,触发该事件                   。 Event: setup .setupPrimary() 方法执行后触发

? 这些事件大多跟 child_process 模块的事件相关        ,在进程间消息传递的基础上完成的封装            。

使用 Node 构建集群能够充分利用多核CPU的计算性能         ,而 child_process 模块的进程间通信和多种事件能够极大提升Node的稳定性      。但进程间无法共享资源                   ,进程间通信有局限性和性能问题                  。此时就需要引入更轻量级的线程了               。

Worker threads多线程

V8 多线程模型

众所周知            ,JavaScript 在运行时是单线程的   。但 JavaScript 的 Runtime V8 引擎却不是单线程的                  。大致包括以下几个线程:

JavaScript 主线程:编译              、执行代码                  。 编译线程:当主线程在执行时      ,编译线程可以优化代码。 Profiler 线程:记录方法耗时的线程               。 其它线程:比如支持并行 GC 的多线程                     。 libuv线程池                  ,默认四个线程               ,全局共享   ,可以将异步操作和计算密集任务交给它执行    。

对于 Node 来说                  ,crypto 这种 CPU 密集 和 fs 这种 I/O 密集的任务是在 libuv线程池 中进行的            。其执行模型是单独创建一个进程                  ,在这个进程中同步执行任务,然后将结果返回到 Event Loop 中               ,Event Loop 可以通过回调函数获取并使用结果                    。

const fs = require("fs"); fs.writeFile(./target.txt, hello Node.js, (err) => { if (err) throw err; console.log(文件已被保存); });

使用非阻塞方法                     ,长耗时的方法不会阻塞主进程之后的代码    ,只需告诉 Worker Pool 去执行该命令            ,并将结果返回给预先设置好的回调函数                    ,在计算完成时触发即可        。

? 由于 Worker Pool 运行在 libuv线程池 中        ,主线程的 Event Loop 不会被阻塞         。能够充分利用 CPU 资源                   。

多线程支持

Node v10.5.0 提供了 Worker threads 模块         ,开始支持多线程编程            。在创建出的每个工作线程中                   ,都会包含 V8 和 libuv            ,即都包含Event Loop:

你可以通过下面这段简单的代码来体验一下:

// main.js const { Worker, isMainThread } = require(worker_threads); if (isMainThread) { console.log("Im main thread: ", isMainThread); // create subThread new Worker(__filename); } else { console.log("Im not main thread: ", isMainThread); // subThread destroy }

我们在主线程中调用new方法创建了一个子线程      ,子线程执行完自动销毁      。最后执行结果如下:

? 合理使用子线程                  ,你能充分调用和分配资源                  。对于有计算密集型需求的应用               ,这是一个重要的优化手段               。另外   ,由于频繁地创建                    、销毁一个线程的开销很大                  ,你可以创建线程池来解决这个问题   。

总结

通过构建集群                  ,你能够充分调用CPU资源,赋予Node更强劲的性能                  。而利用多线程模型               ,将长耗时的任务交由子线程来处理                     ,你能合理分配程序运行资源                  。

目前为止    ,我们介绍完了 Node 的网络      、IO          、进程模块            ,还剩下异步编程和Event Loop两个重点。另外                    ,今天在看 Node 文档时发现 Node v19 刚刚发布了        ,v18 即将成为稳定版

以上就是Nodejs 构建Cluster集群多线程Worker threads的详细内容         ,更多关于Nodejs 构建Cluster多线程的资料请关注本站其它相关文章!

声明:本站所有文章                   ,如无特殊说明或标注            ,均为本站原创发布               。任何个人或组织      ,在未征得本站同意时                  ,禁止复制                     、盗用         、采集      、发布本站内容到任何网站                     、书籍等各类媒体平台                     。如若本站内容侵犯了原著者的合法权益               ,可联系我们进行处理    。

创心域SEO版权声明:以上内容作者已申请原创保护,未经允许不得转载,侵权必究!授权事宜、对本内容有异议或投诉,敬请联系网站管理员,我们将尽快回复您,谢谢合作!

展开全文READ MORE
索尼lt28h手机参数(【索尼LT29i】索尼(SONY)LT29i 3G手机(黑色)WCDMA/GSM 【行情 报价 价格 评测】)