目录
一. 概述
二. 线程与进程
三. cluster 模块源码解析
3.1 起步
3.2 入口
3.3 主进程模块 master.JS
3.4 子进程模块 child.JS
四. 小结
示例代码托管在: http://www.github.com/dashnowords/blogs
博客园地址:《大史住在大前端》原创博文目录
华为云社区地址:[你要的前端打怪升级指南]
一. 概述
cluster 模块是 node.JS 中用于实现和管理多进程的模块. 常规的 node.JS 应用程序是单线程单进程的, 这也意味着它很难充分利用服务器多核 CPU 的性能, 而 cluster 模块就是为了解决这个 问题的, 它使得 node.JS 程序可以以多个实例并存的方式运行在不同的进程中, 以求更大地榨取服务器的性能. node.JS 在官方示例代码中使用 worker 实例来表示主进程 fork 出的子进程, 使得前端开发者在学习过程中非常容易和浏览器环境中的 worker 实现的多线程混淆. 为了容易区分, 我们和 node 官方文档使用一致的名称, 用集群中的 master 和 worker 来区分主进程和工作进程, 用 worker_threads 来描述工作线程.
node.JS 的主从模型中, master 主进程相当于一个包工头, 主管监听端口, 而 slave 进程被用于实际的任务执行, 当任务请求到达后, 它会根据某种方式将连接循环分发给 worker 进程来处理. 理论上, 如果根据当前各个 worker 进程的负载状况或者相关信息来挑选工作进程, 效率应该比直接循环发放要更高, 但 node.JS 文档中声明这种方式由于受到操作系统调度机制的影响, 会使得分发变得不稳定, 所以会将 "循环法" 作为默认的分发策略.
关于 cluster 模块的用法和 API 细节, 可以直接参考官方文档《Node.JS 中文网 V10.15.3/cluster》 http://nodejs.cn/api/cluster.html .
二. 线程与进程
想要尽可能利用服务器性能, 首先需要了解 "线程"(thread) 和 "进程"(process) 这两个概念.
计算机是由 CPU 来执行计算任务的, 如果你只有一个 CPU, 那么这台机器上所有的任务都将由它来执行. 它既可以按照串联执行的原则一个接一个执行任务, 也可以依据并联原则同步执行多个任务, 多个任务同步执行时, CPU 会快速在多个线程之间进行切换, 切换线程的同时要切换对应任务的上下文, 这就会造成额外的 CPU 资源消耗, 所以当线程数量非常多时, 线程切换本身就会浪费大量的 CPU 资源. 如果在执行一个任务的同时, CPU 和内存都还有充足的剩余, 就可以通过某种方式让它们去执行其他任务.
你可以将 "线程" 看作是一种轻量级的 "进程".
如果你在操作系统中打开任务管理器, 在进程标签下就可以看到如下图的示例:
我们可以看到每一个程序至少开辟一个新的进程 (你可能瞬间就明白了 Chrome 效率高的原因, 我什么都没说), 它是一种粒度更大的资源隔离单元, 进程之间使用不同的内存区域, 无法直接共享数据, 只能通过跨进程通讯机制来通讯, 而且由于要使用新的内存区域, 它的创建销毁和切换相对而言都更耗时, 它的好处就是进程之间是互相隔离的, 互不影响, 所以你可以一边听音乐一边玩游戏, 而不会因为音乐软件里突然放了一首轻音乐, 结果你游戏里的角色攻击力减半了.
- 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('你好世界 \ n');
- }).listen(8000);
- console.log(` 工作进程 ${process.pid} 已启动 `);
- }
- 'use strict';
- const childOrMaster = 'NODE_UNIQUE_ID' in process.env ? 'child' : 'master';
- module.exports = require(`internal/cluster/${childOrMaster}`);
- #ifdef __POSIX__
- static void DebugProcess(const FunctionCallbackInfo<Value>& args) {
- // 这里的常量参数是通过地址引用的 worker.process.pid
- Environment* env = Environment::GetCurrent(args);
- // 用 pid 做参数获取当前激活的环境变量, 这一步应该是在获取上下文
- if (args.Length() != 1) {// 不合法调用时报错, 没什么可说的
- return env->ThrowError("Invalid number of arguments.");
- }
- CHECK(args[0]->IsNumber());// 检测参数
- pid_t pid = args[0].As<Integer>()->Value();
- int r = kill(pid, SIGUSR1);// 发送 SIGUSR1 信号, 终止了这个子进程
- if (r != 0) {//exit code 为 0 时是正常退出, 子进程未能正常中止时报错
- return env->ThrowErrnoException(errno, "kill");
- }
- }
来源: https://www.cnblogs.com/dashnowords/p/10958457.html