Node.js | cluster 集群

43 阅读3分钟

简述

Node 在 v0.8 时直接引入了 cluster 模块,用以解决多核 CPU 的利用率问题,同时也提供了较完善的 API,用以处理进程的健壮性问题。 cluster 模块调用 fork 方法来创建子进程,该方法与 child_process 中的 fork 是同一个方法。 cluster 模块采用的是经典的主从模型,cluster 会创建一个 master,然后根据你指定的数量复制出多个子进程,可以使用cluster.isMaster 属性判断当前进程是 master 还是 worker (工作进程)。由 master 进程来管理所有的子进程,主进程不负责具体的任务处理,主要工作是负责调度和管理。

在其他语言中,实现多进程或多线程是非常容易的事情,而对于一个node实例,它是单线程进行工作,不过,node仍能实现多进程工作,在读写方面会比单进程效率会提升很多。 在我自己的测试中,读写20000多个文件,共计2G 的数据,不使用多进程的话,需要3分钟左右的时间,而使用了cluster,40秒左右便完成了读写。

cluster简介

cluster模块可以创建共享服务器端口的子进程,主要是利用处理器多核系统,让子进程处理负载任务,简单来说,主进程通过cluster将任务分发给子进程,让多个任务并发执行,这在读写过程中非常适用。 cluster其底层是通过child_process实现的,详细的参考这篇

代码简单结构

代码逻辑:

1、主进程循环创建子进程,并向每一个子进程发送变量

2、子进程收到变量进行相关操作,执行完成后,向主进程发送信息,表示完成

3、主进程收到子进程的信息,执行关闭子进程操作,并输出 进程xx结束

4、所有子进程关闭,代码执行完毕。

var cluster = require("cluster");
// 获取当前处理器的核数
var numCPUs = require("os").cpus().length;
// 判断是否是主进程
if (cluster.isMaster) {
    // 初始化代码,如获取一个文件夹下的目录结构
      let primes = [];
      for (let i = 0; i < numCPUs; i++) {
        // 启动子进程
        const worker = cluster.fork(); 
        //  在主进程中,这会发送消息给子进程
        // 分配写入工作
        worker.send({
            // 发送变量
        });
      }

  // 当任何一个工作进程关闭的时候,cluster 模块都将会触发 'exit' 事件
  cluster.on("exit", function (worker, code, signal) {
    console.log("进程" + worker.process.pid + "结束");
  });
  // 当主进程收到信息时,执行一些操作,这里是主进程收到子进程消息便将子进程关闭   
  cluster.on("message", function (worker, message, handle) {
    worker.kill();
  });
} else {
  // 监听主进程发来的变量信息,并执行相关操作
  process.on("message", (msg) => {
    // 做些什么   
    // 子进程中,发送消息给主进程
    process.send({ data: "结束" });
  });
}

注意事项

当你想初始化一些内容时,一定要写到这里面,如果你判断外面,每一个子进程都将执行这些代码,导致重复初始化

if (cluster.isMaster) {
// 初始化代码
}

1、关于子进程的数量,我默认是根据核数开启等数量的子进程,网上有其他人测试,开启12个速度最快,但我实测是等核数比开12个快一点,在这个问题上看个人实测结果做选择

2、子进程中变量传递是一个问题,公共变量往往在子进程中无法使用,可以使用子进程向主进程通信解决该问题。