跳至主要內容

Cluster

樱桃茶大约 78 分钟

Clusteropen in new window

Node.js 中的 Cluster 模块允许你简单地创建子进程,这些子进程可以共享同一端口号。它主要用于提高性能和实现负载均衡,尤其是在多核 CPU 系统上。

假如你有一个 Node.js 应用,通常它会运行在单个 CPU 核心上。但如果你的服务器有多个核心,那么你可以用 Cluster 模块来利用这些额外的核心。Cluster 模块会启动一个主进程(master process),主进程再生成多个子进程(worker processes),每个子进程都是应用的一个实例。这样,每个核心都可以被利用起来,提高了计算机的资源利用率和应用的吞吐量。

下面是一个使用 Cluster 模块的基本例子:

const cluster = require('cluster');
const http = require('http');
const numCPUs = require('os').cpus().length; // 得到CPU的核心数

if (cluster.isMaster) {
  console.log(`Master ${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(8000);

  console.log(`Worker ${process.pid} started`);
}

在这个例子中,我们首先引入了必要的模块,并检查了当前的执行环境是否为主进程(cluster.isMaster)。

如果是主进程,我们就会打印出一条消息,然后遍历 CPU 核心数量,对于每一个核心,我们调用 cluster.fork() 方法来创建一个新的工作进程。

如果进程不是主进程,也就意味着这段代码是在工作进程中执行的,我们将创建一个 HTTP 服务器。这个服务器会监听所有工作进程共享的 8000 端口。

当其中一个工作进程退出时(例如因为崩溃或者被关闭),'exit'事件会被触发,这就是我们监听该事件并打印出哪个工作进程退出的原因。

以上是一个非常基础的 Cluster 模块的应用示例,根据不同的需求,Cluster 模块还可以更加复杂和强大。例如,你可以根据具体的业务需要来决定分配给每个工作进程的任务,也可以实现更加智能的负载均衡策略等。

How it worksopen in new window

Node.js 是一个基于 Chrome V8 引擎的 JavaScript 运行环境,它可以让你使用 JavaScript 来编写服务器端代码。Node.js 的 Cluster 模块是其内置模块之一,它允许你轻松创建子进程(称为工作进程),这些子进程可以共享同一个服务器端口。

首先解释一下单线程和多线程:

  • 单线程意味着一次只能执行一个任务。
  • 多线程意味着可以同时执行多个任务。

默认情况下,Node.js 运行在单线程模式中。但在实际应用中,当我们需要处理大量并发连接时,一个单独的线程可能会成为性能瓶颈。Cluster 模块就是为了解决此问题而设计的。

如何工作:

  • 当 Node.js 运行 cluster 模块并调用 cluster.fork() 方法时,它会启动一个与主进程相同的新进程。这个新进程被称为工作进程。
  • 主进程不负责具体的业务处理,而是负责管理工作进程,并且可以根据需要创建多个工作进程。
  • 所有工作进程都是独立的进程,它们在不同的 CPU 核心上运行,并且都有自己的 V8 实例和内存空间,这样它们就不会互相干扰。
  • 这些工作进程通过 IPC(Inter-Process Communication,进程间通信)与主进程通信。
  • 当一个工作进程死掉(比如由于错误崩溃),主进程可以检测到这个事件并重新启动一个新的工作进程来替代它。

实际运用的例子:

  1. HTTP 服务器负载均衡: 假设你有一个高流量的网站,如果只使用一个 Node.js 实例,那么所有的请求都会落在一个 CPU 核心上,这显然不是最有效的利用资源。使用 Cluster 模块,你可以启动和 CPU 核心数相等的工作进程数,这样每个核心都可以处理请求,从而提高性能。
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('hello world\n');
  }).listen(8000);

  console.log(`工作进程 ${process.pid} 已启动`);
}
  1. 长时间运行的计算密集型任务: 如果你的应用程序需要进行大量计算,例如视频编码或复杂的数据分析,那么可以将这些任务分布到多个工作进程中去执行,以免阻塞单个线程。

  2. 服务的稳定性和故障恢复: 在生产环境中,一个服务可能因为各种原因挂掉。使用 Cluster 模块,即使一个工作进程崩溃,其他的工作进程还在运行,主进程可以立即启动一个新的工作进程,从而保证服务的高可用性。

总结一下,Node.js 的 Cluster 模块通过允许多个工作进程共享服务器端口和负载均衡,帮助应用程序更好地利用多核系统,提高性能和可靠性。对于希望在 Node.js 中进行更高效的并发处理的开发者来说,这是一个非常有用的特性。

Class: Workeropen in new window

Node.js 中的 Worker 类是 cluster 模块的一部分。在介绍这个类之前,需要先了解几个概念。

  1. Node.js 是单线程的: Node.js 在默认情况下运行在单个线程中,这意味着所有的用户代码都在同一个线程上执行。对于 I/O 密集型操作(如网络请求或文件操作)来说,这种模式非常高效,因为 Node.js 可以异步处理这些操作,不会阻塞主线程。

  2. 多核 CPU 的利用: 现代服务器通常都有多核心 CPU。由于 Node.js 应用程序默认只在一个核心上运行,我们不能充分利用多核系统的全部计算能力。

  3. Cluster 模块: 为了解决上述问题,Node.js 提供了 cluster 模块,允许开发者创建多个进程,这些进程可以共享同一个服务器端口。每个进程都独立运行在自己的线程上。通过这种方式,一个 Node.js 应用可以在多核 CPU 上并行运行,提高性能和吞吐量。

现在,我们来看 Worker 类。在 cluster 模块中,当主进程创建一个子进程时,这个子进程被称为 Worker。每个 Worker 实际上是 Node.js 应用的一个实例,它们可以接收和处理客户端请求。每个 Worker 都是独立的,如果其中一个崩溃了,它不会影响其他的 Worker,因此这种架构也提高了应用程序的可靠性。

下面是一个使用 cluster 模块和 Worker 类的简单例子。假设我们要建立一个简单的 HTTP 服务器,但是希望它能有效利用服务器上的所有 CPU 核心。

const cluster = require('cluster');
const http = require('http');
const numCPUs = require('os').cpus().length; // 获取 CPU 的核心数

if (cluster.isMaster) {
    console.log(`Master ${process.pid} is running`);

    // Fork workers.
    for (let i = 0; i `<` numCPUs; i++) {
        cluster.fork(); // 创建 Worker 进程
    }

    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(8000);

    console.log(`Worker ${process.pid} started`);
}

这段代码首先检查当前进程是否是主进程(通过 cluster.isMaster)。如果是,它会根据 CPU 的核心数量来创建相应数量的工作进程(cluster.fork())。每个工作进程都将运行这段代码的剩余部分,并监听同一个端口(8000),但在它们自己的执行线程上。

当一个 Worker 进程结束时(可能是因为任务完成或者出错退出),'exit' 事件会被触发。在这个例子中,主进程将会打印出一个信息,告诉你哪个 Worker 结束了它的生命周期。

简而言之,Worker 类在 Node.js 的 cluster 模块中表示一个可以独立运行、处理任务的子进程,主要用于在多核 CPU 系统上提升 Node.js 应用的性能和吞吐量。

Event: 'disconnect'open in new window

好的,让我们来谈谈 Node.js 中cluster模块的'disconnect'事件。

首先了解一下 Node.js 中的cluster模块。这个模块允许你轻松创建共享服务器端口的子进程(称为工作进程)。在单核 CPU 上运行时,Node.js 是单线程的。但是在具有多核 CPU 的系统上,为了充分利用所有核心,可以使用cluster模块来启动一个主进程(master process)和多个工作进程(worker processes),从而能够并行处理更多的任务。

当我们在谈到'disconnect'事件的时候,我们指的是当一个工作进程断开与主进程的 IPC 通道时触发的事件。IPC 通道是一种通信方式,它允许运行在同一台机器上的不同进程(这里是主进程和工作进程)相互发送消息或数据。在 Node.js 的cluster模块中,工作进程通过这个 IPC 通道与主进程进行通信。

以下是'disconnect'事件的工作原理:

  1. 当一个工作进程调用process.disconnect()方法或者自然退出导致其 IPC 通道关闭时,'disconnect'事件会被触发。
  2. 主进程可以监听这个事件来知晓某个工作进程已经断开连接。
  3. 这个事件可以用于清理工作、记录日志、重启新的工作进程等。

现在让我们来举一个例子来说明如何使用'disconnect'事件:

假设你正在运行一个 Node.js 应用程序,该程序使用cluster模块来改善性能。你可能希望在一个工作进程不再与主进程通信时得到通知,并且采取相应的措施。代码示例如下:

const cluster = require('cluster');
const http = require('http');
const numCPUs = require('os').cpus().length;

if (cluster.isMaster) {
  console.log(`主进程 ${process.pid} 正在运行`);

  // 衍生工作进程(workers).
  for (let i = 0; i `<` numCPUs; i++) {
    cluster.fork();
  }

  // 如果任何工作进程断开连接,记录日志并启动新的进程。
  cluster.on('disconnect', (worker) => {
    console.log(`工作进程 ${worker.id} 已断开连接`);
    console.log('启动一个新的工作进程...');
    cluster.fork();
  });

} else {
  // 工作进程可以共享同一个TCP连接
  // 在这个例子中,它是一个HTTP服务器
  http.createServer((req, res) => {
    res.writeHead(200);
    res.end('你好世界\n');
  }).listen(8000);

  console.log(`工作进程 ${process.pid} 已启动`);
}

在这段代码中:

  • 如果当前的进程是主进程,它将基于 CPU 的数量创建多个工作进程,并监听'disconnect'事件。
  • 每当一个工作进程断开连接时,主进程会输出一条日志信息,并启动一个新的工作进程来替代失去连接的那个。
  • 如果当前的进程是工作进程,则会创建一个简单的 HTTP 服务器。

这样,即使某个工作进程因为异常情况断开连接,应用程序也可以快速恢复并继续处理客户端请求。这是一种提高 Node.js 应用程序弹性和可靠性的常用做法。

Event: 'error'open in new window

好的,我来解释一下 Node.js 中关于 Event: 'error' 的概念,尤其是在 cluster 模块中的应用。

首先,Node.js 是一个基于 Chrome 的 V8 JavaScript 引擎的 JavaScript 运行环境。它让我们可以在服务器端运行 JavaScript 代码。而 cluster 模块是 Node.js 的一个核心模块,允许你轻松地创建子进程(也称为工作进程或 workers),这些子进程可以共享同一个服务器端口。

在 Node.js 中,事件是非常重要的概念。Node.js 是基于事件驱动的,其中提供了一个 EventEmitter 类,许多 Node.js 的核心 API 都是建立在这个类之上的。当特定的事情发生时,Node.js 会发出事件,并且可以对这些事件进行监听和处理。

说到 Event: 'error',我们通常指的是在某个对象上发生错误时触发的事件。在 cluster 模块中,'error' 事件会在 cluster 主进程里面的任何一个工作进程因为某种错误而导致无法发送消息、被杀死、无法正常工作时被触发。

下面举例说明如何使用 cluster 模块并监听 error 事件:

const cluster = require("cluster");
const http = require("http");

if (cluster.isMaster) {
  // 这部分代码在主进程执行

  // 创建工作进程(worker)
  const worker = cluster.fork();

  // 监听特定的工作进程的 'error' 事件
  worker.on("error", (error) => {
    console.error(`工作进程出错: ${error.message}`);
  });
} else {
  // 这部分代码在工作进程执行

  // 工作进程创建 HTTP 服务器
  http
    .createServer((req, res) => {
      res.writeHead(200);
      res.end("hello world\n");
    })
    .listen(8000);
}

在这个例子中,如果工作进程遇到了无法处理的错误,'error' 事件就会被触发,并且调用 worker.on('error', callback) 方法中定义的回调函数。在回调函数中,你可以根据错误的信息来进行相应的错误处理,比如记录日志、清理资源、重启工作进程等。

注意,这只是一个简单的例子,实际应用中可能需要更复杂的错误处理机制以确保系统的鲁棒性。

总结一下:在 Node.js 的 cluster 模块中,'error' 事件是当工作进程遇到错误时触发的,开发者可以通过监听这个事件来做出相应的错误处理措施,以维护应用程序的稳定运行。

Event: 'exit'open in new window

当然,很乐意为你详细解释 Node.js 中的 Event: 'exit' 事件。

首先,Event: 'exit' 是在 Node.js 的 cluster 模块中的一个事件。在了解这个事件之前,我们需要先了解一下什么是 cluster 模块。

cluster 模块允许你轻松地创建共享服务器端口的子进程(称为工作进程)。在单核心 CPU 上运行 Node.js 应用程序时,你无法充分利用多核心性能。通过 cluster 模块,Node.js 能够在多核心系统上启动多个工作进程,这样就可以实现负载平衡并提高性能。

现在,让我们看看 Event: 'exit'

cluster 模块中,当一个工作进程死亡(退出)时,'exit' 事件会被触发。这可能是因为工作进程正常退出、由于错误而退出或被信号杀死。

这里有个简单的例子来说明 Event: 'exit' 的使用:

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} 已启动`);
}

在这段代码中:

  1. 我们引入了必要的模块:clusterhttpos

  2. 使用 cluster.isMaster 来检查当前运行的是否是主进程。主进程是负责管理工作进程的进程。

  3. 如果当前是主进程,我们将打印主进程的 PID,并且为每个 CPU 的内核创建一个新的工作进程。这是通过 cluster.fork() 方法完成的。

  4. 当工作进程退出时,cluster.on('exit', ...) 监听器函数会被调用,并打印一条消息说哪个工作进程已经退出了。在监听器回调中,我们得到三个参数:worker 是退出的工作进程的引用,code 是退出码(如果工作进程正常退出的话),signal 是如果工作进程是被信号杀死的话,那么这个参数是信号的名称。

  5. 如果当前不是主进程,我们则认为它是一个工作进程,工作进程会启动一个 HTTP 服务器。

这就是 Event: 'exit' 在 Node.js cluster 模块中的应用。基本上,当你在多核心环境中部署 Node.js 应用并且想要监控和管理工作进程生命周期时,这个事件非常有用。比如,你可能会在工作进程退出后重启一个新的工作进程,以确保服务的可用性。

Event: 'listening'open in new window

Node.js 中的 cluster 模块允许你简单地创建共享服务器端口的子进程,这些子进程可以并行运行在多核系统上。这样可以提高应用程序的效率和响应速度,因为它们可以同时处理更多的任务。

事件:'listening'

当你使用 cluster 模块并且子进程中的服务器开始监听请求时,会触发 'listening' 事件。这个事件在一个工作进程(子进程)调用 listen() 方法后,成功监听所指定端口的时候由主进程(父进程)触发。主要用途是让你知道子进程已经准备好接收请求了。

下面我们来看一下如何使用 cluster 模块,并举例说明 'listening' 事件的应用。

首先,你需要引入 Node.js 的 cluster 模块和其他一些必需的模块,比如 http

const cluster = require("cluster");
const http = require("http");
const numCPUs = require("os").cpus().length; // 获取 CPU 的核心数

然后,判断当前运行的是主进程还是工作进程:

if (cluster.isMaster) {
  console.log(`Master ${process.pid} is running`);

  // 衍生工作进程。
  for (let i = 0; i `<` numCPUs; i++) {
    cluster.fork();
  }

  // 当工作进程已经启动并监听端口时,触发 'listening' 事件。
  cluster.on('listening', (worker, address) => {
    console.log(`Worker ${worker.process.pid} is listening on ${address.address}:${address.port}`);
  });

} else {
  // 工作进程可以共享任何 TCP 连接。
  // 在本例中,它是一个 HTTP 服务器。
  http.createServer((req, res) => {
    res.writeHead(200);
    res.end('hello world\n');
  }).listen(8000);

  console.log(`Worker ${process.pid} started`);
}

在上述代码中,如果当前进程是主进程(cluster.isMaster 返回 true),那么我们将为每个 CPU 核心创建一个工作进程。通过调用 cluster.fork() 来创建工作进程。

紧接着,我们为主进程添加了对 'listening' 事件的监听,当某个工作进程成功监听端口时,触发该事件,cluster.on('listening', callback) 中的回调函数被执行,其中 worker 参数代表工作进程对象,address 包含了工作进程监听

Event: 'message'open in new window

在 Node.js 中,cluster 模块允许你容易地创建共享同一服务器端口的子进程。这些子进程被称为工作进程(worker processes),它们可以用来提高应用程序的性能和可靠性,尤其是在多核 CPU 系统上。

Event: 'message'

当我们谈论 cluster 模块中的 'message' 事件时,我们指的是父进程与其派生的工作进程之间通信的能力。工作进程可以向父进程发送消息,反之亦然。当一个进程接收到另一个进程发来的消息时,它会触发 'message' 事件。

下面是几个实际应用的例子:

实例 1: 工作进程向主进程发消息

假设你有一个简单的 web 应用程序,你希望记录每当有新的用户连接时。你可以让每个工作进程在处理新的用户连接时向主进程发送一条消息。

// 在主进程中
const cluster = require('cluster');
const numCPUs = require('os').cpus().length;

if (cluster.isMaster) {
    console.log(`主进程 ${process.pid} 正在运行`);

    // 衍生工作进程。
    for (let i = 0; i `<` numCPUs; i++) {
        const worker = cluster.fork();

        // 接收来自工作进程的消息。
        worker.on('message', (msg) => {
            console.log(`主进程收到来自工作进程 ${worker.process.pid} 的消息: `, msg);
        });
    }
} else {
    // 工作进程可以共享任何 TCP 连接。
    // 在本例中,它是一个 HTTP 服务器。
    const http = require('http');

    http.createServer((req, res) => {
        res.writeHead(200);
        res.end('hello world\n');
        // 向主进程发送消息。
        process.send({ cmd: 'notifyRequest' });
    }).listen(8000);

    console.log(`工作进程 ${process.pid} 已启动`);
}

在这个例子中,工作进程通过调用 process.send() 发送了一个消息对象给主进程,而主进程通过监听 message 事件来接收这些消息。

实例 2: 主进程向工作进程发消息

以下是如何从主进程发送数据到特定工作进程的示例:

if (cluster.isMaster) {
  const worker = cluster.fork();

  // 发送消息到工作进程
  worker.send({ hello: "world" });
} else {
  process.on("message", (msg) => {
    console.log("消息来自主进程:", msg);
  });
}

在这个例子中,主进程通过worker.send()向工作进程发送了一个包含 { hello: 'world' } 对象的消息,在工作进程中通过监听 process.on('message') 接收并处理来自主进程的消息。

使用消息传递机制,您可以在不同的工作进程之间分配任务,或者汇总计算结果,从而有效利用多核系统资源,提高应用程序的性能。

Event: 'online'open in new window

Node.js 是一个基于 Chrome V8 引擎的 JavaScript 运行环境,它允许你在服务器端运行 JavaScript。Node.js 中有一个内置的模块叫作 cluster,这个模块可以帮助我们更有效地利用多核系统的计算资源。通过 cluster 模块,你可以创建一组工作进程(workers),这些进程可以共享同一个服务器端的 TCP 连接。这是实现负载均衡的一种常用手段。

当你使用 Node.js 的 cluster 模块时,可以监听一系列的事件,其中之一就是 'online' 事件。这个事件是在一个工作进程被 fork 出来并且在线上能够响应消息后触发的。

下面我会给你解释 online 事件,并举例说明如何使用它。

Event: 'online'

当使用 cluster.fork() 创建新的工作进程后,一旦工作进程已经启动并且可以接收消息,'online' 事件便会被触发。这意味着工作进程已经准备好处理任务了。

实例

假设你有一个 Node.js 应用程序,想要充分利用多核 CPU,在不同的核上运行多个实例以提高性能。这里是一个简单的例子,演示如何使用 cluster 模块和监听 'online' 事件:

const cluster = require('cluster');
const http = require('http');
const numCPUs = require('os').cpus().length;

if (cluster.isMaster) {
    console.log(`主进程 ${process.pid} 正在运行`);

    // Fork 工作进程
    for (let i = 0; i `<` numCPUs; i++) {
        cluster.fork();
    }

    cluster.on('online', (worker) => {
        console.log(`工作进程 ${worker.process.pid} 已上线`);
    });

    cluster.on('exit', (worker, code, signal) => {
        console.log(`工作进程 ${worker.process.pid} 已退出`);
    });

} else {
    // 工作进程可以共享任何TCP连接。
    // 在本例中,它是一个 HTTP 服务器
    http.createServer((req, res) => {
        res.writeHead(200);
        res.end('hello world\n');
    }).listen(8000);

    console.log(`工作进程 ${process.pid} 已启动`);
}

在这段代码中:

  1. 我们先检查当前进程是否为主进程(cluster.isMaster)。
  2. 如果是主进程,就输出一条信息,然后根据 CPU 核心数目 (numCPUs) 来 fork 出相应数量的工作进程。
  3. 对于每个被成功创建的工作进程,注册一个监听器到 'online' 事件,以便当该工作进程上线时可以得到通知。
  4. 同样注册 'exit' 事件的监听器,以便在工作进程停止或退出时可以得到通知。
  5. 如果当前进程是工作进程,它将启动一个 HTTP 服务器监听在端口 8000 上。

在上面的例子中,每当一个工作进程由于调用 cluster.fork() 方法而成功创建并且变得可用时,都会触发 'online' 事件,并打印出该工作进程的 PID,表明它已经准备好接收任务了。这样,你就能够确保所有工作进程都已经启动完成,并且处于运行状态,准备开始处理请求。

worker.disconnect()open in new window

好的,了解 Node.js 中worker.disconnect()这个方法之前,你需要先知道在 Node.js 中可以使用cluster模块来创建一个父进程(通常称为“master”进程),它可以管理多个子进程(称为“worker”进程)。这些 worker 进程能够并行地运行同一份代码,并处理任务,比如处理网络请求等。

现在来说说worker.disconnect()这个方法。当你想要平滑地停止一个 worker 进程时候,可以调用该方法。调用后,它会关闭 worker 进程中服务器的所有连接,并等待这些连接自然结束(即客户端完成请求并得到响应),再关闭 worker 进程。这样可以避免因为直接终止进程而导致正在处理的请求失败,提供了更加优雅的停机方式。

具体使用场景包括:

  1. 负载调整:可能你的应用在低峰期不需要那么多 worker 进程,为了节约资源,你可能会减少 worker 的数量。
  2. 代码更新:如果你更新了应用代码,需要重新启动 workers 以加载新的代码,你可以依次断开每个 worker,然后创建新的 worker 替换它们,这样可以实现 0 停机时间更新。
  3. 异常恢复:如果检测到某个 worker 进程出现问题,例如内存泄漏或者其他异常状态,你可以断开有问题的 worker,替换为新的进程,保证系统的稳定性。

下面举一个简单的例子来说明怎样使用worker.disconnect()

const cluster = require("cluster");
const http = require("http");

if (cluster.isMaster) {
  // 这是Master进程代码

  // 创建一个worker进程
  const worker = cluster.fork();

  // 一段时间后,我们想平滑地停止这个worker
  setTimeout(() => {
    worker.disconnect(); // 调用disconnect方法

    // 可以设置超时强制退出
    const timeout = setTimeout(() => {
      worker.kill(); // 如果worker没有在合理时间内关闭,就强制终止
    }, 5000);

    // 监听worker的'disconnect'事件
    worker.on("disconnect", () => {
      clearTimeout(timeout); // 清除超时,因为worker已经平滑退出
      console.log(`Worker #${worker.id} has disconnected`);
    });
  }, 10000);
} else {
  // 这是Worker进程代码

  // 创建HTTP服务
  http
    .createServer((req, res) => {
      res.writeHead(200);
      res.end("hello world\n");
    })
    .listen(8000);
}

在这个例子里,主进程创建了一个 worker,worker 创建了一个监听 8000 端口的 HTTP 服务器。10 秒钟之后,主进程调用worker.disconnect()来告诉 worker 停止接受新的连接,但已经建立的连接会继续服务直至结束。如果 worker 在规定时间内没有正常断开连接,则通过worker.kill()强制结束进程。

worker.exitedAfterDisconnectopen in new window

worker.exitedAfterDisconnect 是 Node.js 中 cluster 模块的一个属性,用于判断工作进程(Worker)是由于主动断开连接(disconnect)退出(exited),还是因为其他原因意外退出。了解这个概念之前,我们需要先简单了解一下 Node.js 的 cluster 模块。

Node.js 是一个可以在服务器上运行 JavaScript 代码的平台。默认情况下,Node.js 运行在单线程上,但它提供了一个叫做 cluster 的模块,允许你能够轻松创建子进程(称为“工作进程”或“Workers”),并利用多核心 CPU 的优势来处理更多的负载。

当你使用 cluster 模块时,会有一个主进程(Master)和多个工作进程(Workers)。主进程负责管理工作进程,而工作进程则接收并处理请求。在某些情况下,你可能希望主动断开一个工作进程的连接,比如为了重启进程或关闭不再需要的服务。

现在,让我们来看看 worker.exitedAfterDisconnect 的具体作用:

  1. 当一个工作进程被主进程的方法 .disconnect() 主动断开连接后,如果该工作进程随后退出,那么该进程的 worker.exitedAfterDisconnect 属性将会被设置为 true
  2. 如果工作进程是因为其他原因(如崩溃、错误等)退出的,那么 worker.exitedAfterDisconnect 将会是 false

通过检查 worker.exitedAfterDisconnect 的值,你可以在编写代码时进行条件判断,从而确定是否需要重新启动一个新的工作进程来替换退出的进程。

实际运用的例子

假设你正在运行一个 Node.js 应用程序,该应用程序使用 cluster 模块在四个核心上各运行了一个工作进程。

const cluster = require('cluster');
const http = require('http');

if (cluster.isMaster) {
    console.log(`主进程 ${process.pid} 正在运行`);

    // 衍生工作进程。
    for (let i = 0; i `<` 4; i++) {
        cluster.fork();
    }

    cluster.on('exit', (worker, code, signal) => {
        if (worker.exitedAfterDisconnect === true) {
            console.log(`工作进程 ${worker.process.pid} 被主动断开连接`);
        } else {
            console.log(`工作进程 ${worker.process.pid} 意外退出,退出码为 ${code}`);
            cluster.fork(); // 替换意外退出的工作进程
        }
    });
} else {
    // 工作进程可以共享任何 TCP 连接。
    // 在本例中,它是一个 HTTP 服务器。
    http.createServer((req, res) => {
        res.writeHead(200);
        res.end('你好世界\n');
    }).listen(8000);

    console.log(`工作进程 ${process.pid} 已启动`);
}

在这个例子中,cluster.on('exit', ...) 监听器内部检查了 worker.exitedAfterDisconnect 的值,来确定工作进程是被主动断开连接退出的,还是因为其他原因退出的。如果是意外退出(比如崩溃),则会立即衍生一个新的工作进程来维持服务的可用性。

worker.idopen in new window

worker.id 是 Node.js 中 cluster 模块的一个属性,它用于在 Node.js 应用中实现多进程。首先,我们来了解一下 cluster 模块和它为什么重要。

Node.js 是单线程的,这意味着它默认情况下只能在一个 CPU 核心上运行。对于多核心服务器来说,这可能不是最有效的方式,因为它没有利用到多个核心。这就是 cluster 模块发挥作用的地方,它可以帮助你轻松创建子进程(这些子进程被称为 “工作进程” 或 “workers”),让你的应用可以在多个核心上并行运行。

每个工作进程都有一个唯一的标识符,即 worker.id。当你在主进程中创建工作进程时,cluster 会自动给每个新的工作进程分配一个唯一的 id。你可以使用这个 id 来区分不同的工作进程。

下面我们举例说明:

  1. 创建一个简单的 HTTP 服务器,但使用 cluster 模块让它能够运行多个工作进程:
const cluster = require('cluster');
const http = require('http');
const numCPUs = require('os').cpus().length; // 获取 CPU 的核心数

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} 已启动`);
}

在这个例子中,如果你打印出 worker.id (比如在 cluster.fork() 调用后),你将看到每个工作进程都有不同的 id

  1. 使用 worker.id 来跟踪特定的工作进程:
// ... 主进程代码 ...

if (cluster.isMaster) {
  // ... 主进程的代码 ...

  cluster.on("fork", (worker) => {
    console.log(`工作进程 ${worker.id} 已经被创建`);
  });

  // ... 其他监听事件代码 ...
}

// ... 工作进程代码 ...

在这里,每当一个新的工作进程被创建,'fork' 事件就会被触发,并且 worker.id 会被用来输出哪个工作进程被创建了。

综上所述,worker.idcluster 模块中一个很有用的属性,它允许开发者在构建可扩展的 Node.js 应用程序时,能够轻松地识别和管理多个工作进程。

worker.isConnected()open in new window

worker.isConnected() 是 Node.js 中的一个方法,它用于判断在 cluster 模块中的工作进程(worker)是否仍然连接着主进程(master)。在多核 CPU 上,Node.js 可以通过 cluster 模块创建一组子进程,这些子进程都运行相同的代码,并且可以共享服务器端口。

在使用 cluster 模块时,主进程会负责接受新的连接请求并根据某种策略(例如轮询)将其分配给各个工作进程。这样可以让 Node.js 应用程序更好地利用多核 CPU 的能力,提高性能和吞吐量。

现在来解释 worker.isConnected() 方法:

  • 当你调用 worker.disconnect() 方法来让一个工作进程优雅地断开与主进程之间的 IPC 通道(不是立刻结束进程,而是允许当前工作结束后再断开连接)时,你可以使用 worker.isConnected() 来检查该工作进程是否仍然连接着。
  • 如果工作进程仍然连接着主进程,worker.isConnected() 返回 true,否则返回 false

接下来,让我们看几个具体的应用例子:

例子 1:检查工作进程是否连接

假设我们有一个应用程序,它使用了 Node.js 的 cluster 模块来创建了多个工作进程:

const cluster = require('cluster');
const http = require('http');
const numCPUs = require('os').cpus().length;

if (cluster.isMaster) {
    // 这里是主进程代码
    console.log(`Master ${process.pid} is running`);

    // 分叉(创建)工作进程
    for (let i = 0; i `<` numCPUs; i++) {
        cluster.fork();
    }

    cluster.on('exit', (worker, code, signal) => {
        console.log(`worker ${worker.process.pid} died`);
    });
} else {
    // 工作进程可以共享任何TCP连接
    // 在本例中,它是一个HTTP服务器
    http.createServer((req, res) => {
        res.writeHead(200);
        res.end('hello world\n');
    }).listen(8000);

    console.log(`Worker ${process.pid} started`);
}

在这段代码中,我们使用了 cluster 来创建与 CPU 核心数量相等的工作进程。每个工作进程监听 8000 端口并响应'hello world'。

现在,如果你想检查其中一个工作进程是否仍然连接,可以这样做:

// 假设在主进程中
for (const id in cluster.workers) {
  const worker = cluster.workers[id];
  if (!worker.isConnected()) {
    console.log(`工作进程 ${worker.process.pid} 已经断开连接`);
  }
}

例子 2:在断开连接前发送消息

你也可以在工作进程即将断开连接前进行一些操作,比如发送一条告别消息:

// 在主进程中
cluster.on("disconnect", (worker) => {
  if (worker.isConnected()) {
    worker.send("Goodbye, your work was appreciated!");
  }
});

在这段代码里,当工作进程断开连接时,主进程会检查它是否依然连接,如果是,就会向它发送一条消息。

请注意,实际编程时可能需要处理多种复杂情况,例如异常处理、资源清理等等。但基本上,worker.isConnected() 就是这么一个用来检查工作进程与主进程连接状态的工具。

worker.isDead()open in new window

好的,让我来详细解释一下 Node.js 中 worker.isDead() 这个函数的用途和工作原理。在 Node.js 中,有一个模块叫做 cluster。这个模块允许你轻松创建子进程(也叫 workers),它们可以共享服务器端口。

当你使用 cluster 模块创建了多个 worker 之后,你可能会想知道这些 worker 是否还活着(即,是否还在运行中)。这就是 worker.isDead() 函数用来检查的。如果调用这个函数后返回 true,那么表示这个 worker 进程已经结束或者不再可用了;如果返回 false,那么表示这个 worker 还在正常运行。

在实际应用中,了解 worker 是否死掉非常重要,因为你可能需要根据这个信息来决定是否要重启一个 worker,以确保服务的可用性。例如,在处理网络请求时,如果一个 worker 因为某些原因崩溃了,你可能想要立刻启动一个新的 worker 来替代它,以防整个服务出现停机。

下面是 worker.isDead() 使用的一个简单示例:

const cluster = require('cluster');
const http = require('http');
const numCPUs = require('os').cpus().length;

if (cluster.isMaster) {
  console.log(`主进程 ${process.pid} 正在运行`);

  // 衍生 worker 进程。
  for (let i = 0; i `<` numCPUs; i++) {
    cluster.fork();
  }

  cluster.on('exit', (worker, code, signal) => {
    console.log(`worker ${worker.process.pid} 已退出`);

    // 检查 worker 是否“死亡”
    if (worker.isDead()) {
      console.log(`worker ${worker.process.pid} 被标记为死亡`);
    }

    // 可以在这里重启 worker
    cluster.fork();
  });
} 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(8000);

  console.log(`worker ${process.pid} 已启动`);
}

在这个例子中,我们首先检查当前的执行环境是否是 master 进程。如果是,我们就创建几个 worker 子进程。然后,我们监听 exit 事件,这个事件会在任何 worker 停止时触发。当一个 worker 停止时,我们会利用 worker.isDead() 函数来检查这个 worker 是否真的不再运行了。如果确实“死亡”,我们则输出相关信息,并且可以选择重新 fork(创建)一个新的 worker 来继续提供服务。

注意:在实际的生产环境中,处理 worker 的崩溃和重启通常会更加复杂,因为你需要考虑到错误日志记录、限制重启次数、延迟重启等因素。但是上面的例子展示了 worker.isDead() 在基本层面的使用方式。

worker.kill([signal])open in new window

worker.kill([signal]) 是 Node.js 中的一个方法,它用于终止在 Cluster(集群)模式下运行的 Worker 进程。Cluster 模块允许你创建一组共享同一服务器端口的子进程(称为 workers),这可以提高应用程序在多核 CPU 系统上的效率。

首先,了解几个概念非常重要:

  1. Node.js: 一个基于 Chrome V8 JavaScript 引擎的 JavaScript 运行时环境,它允许你在服务器端运行 JavaScript 代码。

  2. Cluster 模块: Node.js 的一个内置模块,它可以让你轻松地创建子进程,这些子进程可以运行相同的服务器代码并共享同一端口。

  3. Worker 进程: 由 Cluster 模块创建的子进程,每个子进程都可以处理部分网络请求,多个 Worker 可以提高应用的性能和吞吐量。

  4. 信号(Signal): 一种 IPC(进程间通信)机制,用于向进程发送简单的通知。例如,SIGTERM 用来优雅地停止进程,而 SIGKILL 是用来立即结束进程的。

现在来具体看 worker.kill([signal]) 方法:

  • worker: 表示一个由 Cluster 模块创建的 Worker 实例。
  • .kill(): 是这个 Worker 实例的一个方法,调用它将会终止该 Worker 进程。
  • [signal]: 是一个可选参数,你可以指定一个特定的信号来终止 Worker 进程。如果这个参数被省略,默认使用的是 SIGTERM

实际应用的例子

假设你正在运行一个 Node.js 应用程序,该应用程序通过 Cluster 模块用多个核心处理网络请求。你可能需要在某些情况下终止一个 Worker,比如它变得不响应或者你想要手动重新启动 Workers 来加载新的代码。

以下是一个简单的例子:

const cluster = require('cluster');
const http = require('http');
const numCPUs = require('os').cpus().length;

if (cluster.isMaster) {
  // 主进程分支
  console.log(`主进程 ${process.pid} 正在运行`);

  // 衍生 worker 进程。
  for (let i = 0; i `<` numCPUs; i++) {
    cluster.fork();
  }

  cluster.on('exit', (worker, code, signal) => {
    console.log(`worker ${worker.process.pid} 已退出`);
  });
} else {
  // Workers 可以共享任何 TCP 连接
  // 在本例中,它是一个 HTTP 服务器
  http.createServer((req, res) => {
    res.writeHead(200);
    res.end('你好世界\n');
  }).listen(8000);

  console.log(`Worker ${process.pid} 已启动`);

  // 模拟工作进程出现问题需要被杀死
  if (process.pid % 2 === 0) { // 如果 pid 是偶数,表示我们只是为了示例要杀死它
    setTimeout(() => {
      // 假设这个 Worker 需要被关闭
      process.kill(process.pid, 'SIGTERM');
    }, 10000); // 在 10 秒后终止这个 Worker
  }
}

// 在主进程中,你可以决定在什么时候杀死 workers
setTimeout(() => {
  for (const id in cluster.workers) {
    if (cluster.workers[id].process.pid % 2 !== 0) {
      // 让主进程杀死奇数pid的 Worker
      cluster.workers[id].kill();
    }
  }
}, 12000); // 在 12 秒后开始杀死奇数pid的 Worker

在这个例子中,我们创建了多个 Worker 进程来处理 HTTP 请求。我们在主进程中设置了一个定时器,在一段时间后终止所有 Worker 进程。Worker 进程也可能自己决定在某些条件下终止自己。

当你调用 worker.kill() 方法时,实际上就是发送了一个默认的 SIGTERM 信号到那个 Worker 进程,告诉它应该进行优雅的关闭。如果你需要强制终止一个进程,可以传递 SIGKILL 作为参数给这个方法:worker.kill('SIGKILL')

worker.processopen in new window

Node.js 是一个后端 JavaScript 运行环境,它允许开发者使用 JavaScript 来编写服务器端的代码。在 Node.js 中,有一个模块叫做 cluster,这个模块可以让你轻松地创建共享同一个端口的子进程(workers),这种方式常用于提升性能和实现负载均衡。

cluster 模块中,当我们创建了一个 worker 子进程之后,worker.process 就是一个属性,它提供了对该 worker 对应的底层 child process 的引用。你可以通过这个属性来获取关于 worker 进程的信息,或者与这个进程进行交互。

实际例子

让我们看一个简单的例子来理解 worker.process 在实践中是如何使用的:

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++) {
    const worker = cluster.fork();

    // 使用 worker.process 属性来获取进程信息
    console.log(`工作进程 ${worker.process.pid} 已启动`);
  }

  cluster.on('exit', (worker, code, signal) => {
    console.log(`工作进程 ${worker.process.pid} 已退出`);
  });
} else {
  // 工作进程可以共享任何 TCP 连接。
  // 在本例子中,它是一个 HTTP 服务器
  http.createServer((req, res) => {
    res.writeHead(200);
    res.end('hello world\n');
  }).listen(8000);

  console.log(`工作进程 ${process.pid} 已启动`);
}

上面的代码展示了如何使用 Node.js 的 cluster 模块来创建一个简单的多进程 HTTP 服务器。这里的 numCPUs 变量代表的是 CPU 的核心数,这通常是我们想要创建的进程数。

如果当前执行的是主进程(主线程),我们会打印出其 PID(进程 ID),然后利用 for 循环创建与 CPU 核心数量相等的工作进程。每创建一个 worker,我们就通过 worker.process 打印出那个进程的 PID。

当某个工作进程因为某些原因停止时,我们监听 exit 事件,并再次使用 worker.process 来获取已退出进程的 PID,并打印一条消息。

如果当前执行的是工作进程,我们将创建一个 HTTP 服务器,监听在 8000 端口上的请求,并简单地返回 "hello world" 字符串。

这个例子演示了 worker.process 在管理多个工作进程时的使用情况,尤其是在需要监控、记录或其他与具体工作进程相关的操作时非常有用。

worker.send(message[, sendHandle[, options]][, callback])open in new window

当然,很高兴为你解释 worker.send 这个方法在 Node.js 中的作用和如何使用。

Node.js 和 Cluster 模块

Node.js 是一个基于 Chrome V8 引擎的 JavaScript 运行时环境。Node.js 可以让开发者使用 JavaScript 来编写服务端代码。由于 Node.js 默认是单线程运行的,为了充分利用多核 CPU 的性能,Node.js 提供了一个叫 cluster 的模块。这个模块可以帮助你轻松创建共享同一端口的子进程(称为工作进程或 worker)。

worker.send 方法

cluster 模块中,worker.send 是一个非常重要的通信机制。用这个方法,主进程(master 或 parent process)可以向创建的工作进程发送消息。同样,工作进程也可以通过这种方式回发信息给主进程。

参数解释

  • message: 要发送的消息,可以是任何 JSON 支持的数据类型(例如:对象、数组、字符串、数字等)。
  • sendHandle: (可选)如果你想发送一个连接(比如 TCP socket),可以通过这个参数传递。
  • options: (可选)额外的配置选项,例如可以设定序列化的类型。
  • callback: (可选)当消息被发送后执行的回调函数。

实际例子

假设你有一个需要进行大量计算的服务器程序,并希望利用所有的 CPU 核心来加速处理。

  1. 创建工作进程(Worker):

    const cluster = require('cluster');
    const numCPUs = require('os').cpus().length;
    
    if (cluster.isMaster) {
      // 主进程分支
      console.log(`主进程 ${process.pid} 正在运行`);
    
      // 衍生工作进程
      for (let i = 0; i `<` numCPUs; i++) {
        const worker = cluster.fork();
    
        // 发送消息到工作进程
        worker.send({ msg: 'Hello Worker!' });
      }
    
      cluster.on('exit', (worker, code, signal) => {
        console.log(`工作进程 ${worker.process.pid} 已退出`);
      });
    } else {
      // 工作进程分支
      process.on('message', (msg) => {
        console.log(`工作进程 ${process.pid} 收到消息: `, msg);
      });
    
      console.log(`工作进程 ${process.pid} 已启动`);
    }
    

    在这个例子中,主进程创建多个工作进程,并通过 worker.send 向每个工作进程发送了一个简单的消息 { msg: 'Hello Worker!' }。每个工作进程收到消息后,会打印出来。

  2. 处理回调: 如果你需要确认消息已经被发送,可以提供一个回调函数。

    worker.send({ msg: "Hello Worker with Callback!" }, (error) => {
      if (error) {
        console.error("消息发送出错", error);
      } else {
        console.log("消息已成功发送");
      }
    });
    

    这里我们给 send 方法加上了一个回调函数,这个回调函数会在消息发送完毕后执行,它告诉我们是否发送成功或失败。

  3. 传递 sendHandle: 如果你要发送一个可以在不同进程间共享的 TCP 服务器或 socket,可以这么做:

    // 假设我们有一个TCP服务器或socket 'server'
    worker.send("tcp-server", server);
    

正如你看到的,worker.send 方法是 cluster 模块中用于主工作进程间通信的关键功能。你可以使用它来分配任务、发送数据或者状态信息等。记得消息传递是异步的,所以如果你需要确认消息何时被送达,应该使用回调函数。

Event: 'disconnect'open in new window

Node.js 中的 cluster 模块允许你轻松创建子进程来利用多核 CPU 的能力。这些子进程可以共享同一个服务器端口,并通过父进程(通常称为 master 进程)进行管理。

在使用 cluster 模块时,如果一个工作进程(worker)失去与主进程的连接或是被意外关闭,会触发 'disconnect' 事件。这个事件对于监控工作进程的状态和实现宕机恢复逻辑很重要。

下面我们将具体解释一下这个事件,并举例说明它的应用。

当你在主进程中监听 'disconnect' 事件时,你可以得知某个工作进程已经断开连接,并根据需要采取行动,比如重启一个新的工作进程以保持服务的可用性。这个事件是在工作进程的 IPC(进程间通信)通道关闭后触发的,但此时工作进程可能还在运行。

例如,假设你有一个基于 Node.js 的 web 服务器,你想要利用服务器上所有可用的 CPU 核心。你可以使用 cluster 模块来创建多个工作进程,每个进程都运行服务器代码并能够处理请求。以下是一个简单的示例:

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('disconnect', (worker) => {
    console.error(`工作进程 ${worker.id} 已断开连接`);
  });

} else {
  // 工作进程可以共享任何TCP连接。
  // 在本例中,它是HTTP服务器。
  http.createServer((req, res) => {
    res.writeHead(200);
    res.end('你好世界\n');
  }).listen(8000);

  console.log(`工作进程 ${process.pid} 已启动`);
}

在以上代码中,主进程创建了和 CPU 数量相等的工作进程,然后监听每个工作进程的 'disconnect' 事件。当任何一个工作进程因为某种原因断开连接时,主进程会收到 'disconnect' 事件,并打印出相关信息。

值得注意的是,断开连接并不意味着工作进程已经完全停止;它可能仍然在执行一些后台任务或清理工作。如果你希望在工作进程结束后做一些事情,那么你可能需要监听 'exit' 事件而不是 'disconnect' 事件。

总之,'disconnect' 事件在 Node.js 的 cluster 模块中提供了一种机制,让你能够知道什么时候一个工作进程不再与主进程通信,这样你就可以适当地响应可能出现的问题。

Event: 'exit'open in new window

在 Node.js 中,cluster模块允许你轻松地创建子进程(称为工作进程),这些子进程可以共享同一个服务器端口。使用cluster模块可以帮助你充分利用多核心系统的计算能力,因为默认情况下 Node.js 运行在单个线程上。

当你使用cluster模块启动了一系列的工作进程后,可能需要监听这些进程何时结束或退出。这就是'exit'事件的作用。

Event: 'exit'

'exit'事件在一个工作进程正常退出或者被强制终止(例如:因为没有响应而被杀死)时触发。每次工作进程退出时,都会触发cluster对象上的'exit'事件。

参数

该事件提供了三个参数给事件监听器:

  1. worker: 这是一个表示已经退出的工作进程的对象。
  2. code: 这是一个数字,表示进程退出时的退出代码。如果进程通过调用process.exit退出,那么这将是传递给process.exit的退出码。
  3. signal: 如果工作进程因为接收到一个系统信号而终止,则这个参数是一个字符串,表示那个信号(例如'SIGKILL''SIGTERM'等);如果进程不是由于接收系统信号而终止的,则值为null

示例

下面是一个cluster模块和'exit'事件的简单实例。这个程序会启动几个工作进程,并且当这些工作进程退出时,主进程会收到通知。

const cluster = require('cluster');
const http = require('http');

if (cluster.isMaster) {
    console.log(`主进程 ${process.pid} 正在运行`);

    // 衍生工作进程。
    for (let i = 0; i `<` 2; i++) {
        cluster.fork();
    }

    // 当任何工作进程退出时,打印退出消息。
    cluster.on('exit', (worker, code, signal) => {
        console.log(`工作进程 ${worker.process.pid} 已退出,退出码:${code},信号:${signal}`);
    });
} else {
    // 工作进程可以共享任何 TCP 连接。
    // 在此例中,它是一个 HTTP 服务器。
    http.createServer((req, res) => {
        res.writeHead(200);
        res.end('你好世界\n');
    }).listen(8000);

    console.log(`工作进程 ${process.pid} 已启动`);
}

在这段代码中,我们首先判断当前进程是否为主进程(即原始启动的进程)。如果是,我们打印出一条消息,并启动两个工作进程。然后,我们监听'exit'事件来获取任何工作进程的退出信息。

如果当前进程不是主进程,我们假设它是一个工作进程,并开始一个简单的 HTTP 服务器监听在端口 8000 上。这意味着当我们启动主进程时,会有两个工作进程同时监听同一个端口,处理传入的 HTTP 请求。

当某个工作进程退出时(无论是正常退出还是被信号杀死),主进程的'exit'事件侦听器会被触发,并打印出相应的信息。

这种模式非常适合在多核 CPU 上扩展网络服务的性能,因为它允许单个服务同时在多个核心上并行运行。

Event: 'fork'open in new window

Node.js 是一个基于 Chrome V8 引擎的 JavaScript 运行环境, 它让开发者可以用 JavaScript 编写服务器端的代码。在 Node.js 中,cluster 模块允许你简单地创建子进程来利用多核 CPU 的能力。

当你使用 cluster 模块时,你可以启动一个主进程(master process)。这个主进程可以根据需求,生成一系列工作进程(worker processes),通过这种方式来实现对多核处理器的利用。每个工作进程都是独立的,它们是在新的 Node 实例中运行相同的程序,但是有自己的独立内存空间,因此不会互相影响。

[Event: 'fork'](https://nodejs.org/docs/latest/api/cluster.html#event-fork)

cluster 模块中,'fork' 事件是当一个新的工作进程被创建时触发的。在你调用 cluster.fork() 方法来创建一个新的工作进程后,这个事件就会在主进程中被触发。

例子:

下面我将用一个例子来演示如何使用 cluster 模块,并举例说明 fork 事件的应用。

假设我们想要创建一个简单的 HTTP 服务器,并利用所有可用的 CPU 核心,我们会这样写:

const cluster = require('cluster');
const http = require('http');
const numCPUs = require('os').cpus().length; // 获取 CPU 的核心数

if (cluster.isMaster) {
  console.log(`主进程 ${process.pid} 正在运行`);

  // Fork 工作进程。
  for (let i = 0; i `<` numCPUs; i++) {
    cluster.fork();
  }

  cluster.on('fork', (worker) => {
    console.log(`工作进程 ${worker.process.pid} 已经被创建`);
  });

  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} 已启动`);
}

在上面的代码中:

  1. 我们引入了 clusterhttp 模块。
  2. 我们检查当前进程是否是主进程。这是通过 cluster.isMaster 来确定的。
  3. 如果是主进程,我们输出一条消息表明主进程正在运行。
  4. 接着我们为每个 CPU 核心通过调用 cluster.fork() 创建一个工作进程。
  5. 当每个工作进程被创建时,'fork' 事件被触发,并输出一条消息表明新的工作进程已经被创建。
  6. 如果某个工作进程退出,我们监听 exit 事件,并输出一条消息表明该工作进程已经退出。
  7. 如果当前进程是工作进程,我们创建一个 HTTP 服务器监听在端口 8000 上。

当这段代码运行在支持多 CPU 核心的机器上时,它会为每个核心创建一个工作进程。每个工作进程都是独立运行的,这意味着 Node.js 程序可以更好地并行处理请求,提高性能。

Event: 'listening'open in new window

Node.js 的 cluster 模块允许你创建一个由多个进程组成的应用程序,以利用多核 CPU 的优势。当你在使用这个模块时,可以通过监听各种事件来控制集群的行为和响应相应的情况。

'listening' 事件是 cluster 模块中的一个事件,当工作进程(worker)调用了listen()方法并且服务器开始接受连接时,该事件就会被触发。此时主进程(master)会收到通知说一个工作进程已经准备好接受连接了。

下面举个例子来说明 'listening' 事件的用法:

const cluster = require('cluster');
const http = require('http');
const numCPUs = require('os').cpus().length; // 获取CPU的核心数

if (cluster.isMaster) {
  console.log(`主进程 ${process.pid} 正在运行`);

  // 衍生工作进程。
  for (let i = 0; i `<` numCPUs; i++) {
    cluster.fork();
  }

  // 当一个工作进程启动监听时,就会触发 'listening' 事件。
  cluster.on('listening', (worker, address) => {
    console.log(`工作进程 ${worker.process.pid} 开始监听:地址是 ${address.address}:${address.port}`);
  });
} else {
  // 工作进程可以共享任何 TCP 连接。
  // 在本例子中,它是一个 HTTP 服务器
  http.createServer((req, res) => {
    res.writeHead(200);
    res.end('hello world\n');
  }).listen(8000);
}

在这个例子中,如果我们有 4 个 CPU 核心,主进程将会衍生 4 个工作进程。每个工作进程都会设置一个 HTTP 服务器监听 8000 端口。

当每个工作进程开始监听端口后,主进程就会收到 'listening' 事件,并打印出工作进程的 PID(进程 ID)和它正在监听的地址和端口号。

所以实际上,'listening' 事件提供了一种反馈机制,让你能知道工作进程何时准备好接受外部请求,这对于管理和监控大型应用程序的状态非常重要。

Event: 'message'open in new window

Node.js 是一个基于 Chrome V8 引擎的 JavaScript 运行环境。它允许你使用 JavaScript 来编写服务器端代码。Node.js 中有一个模块叫作 cluster,这个模块允许你轻松地创建共享同一个服务器端口的子进程。

cluster 模块中,'message' 事件是非常重要的一个概念。当你在一个多核 CPU 的机器上运行 Node.js 应用时,你可能想要最大化你的资源利用率,因此你会使用 cluster 模块来创建多个进程(也称作工人进程或 workers)。这些子进程可以并行处理任务,提高应用的性能。

Event: 'message'

  1. 触发时机:当使用 cluster 模块,并且主进程(master)与子进程(worker)之间需要进行通信时,就会触发 'message' 事件。具体来说,当一个 worker 使用 process.send() 发送消息时,主进程会收到消息,并触发 'message' 事件;同样,当主进程发送消息给 worker,worker 上的 process.on('message') 也会捕获这个事件。

  2. 事件参数:'message' 事件接收两个参数:

    • message:一个包含了从另一侧发送过来的信息的对象。
    • handle:一个与发送信息相关联的句柄,比如一个网络连接的句柄。
  3. 使用场景:这个事件通常用于主进程和子进程之间的自定义逻辑交互。例如,主进程可以指派任务给特定的子进程,或者子进程在完成任务后将结果传回主进程。

实际例子

假设我们正在构建一个 web 服务器,该服务器用 Node.js 编写,并且希望它能够处理大量的并发请求。我们可以使用 cluster 模块来创建多个子进程,每个子进程都监听相同的端口,从而有效地分摊负载。

const cluster = require('cluster');
const http = require('http');

if (cluster.isMaster) {
  // 主进程的代码

  const numCPUs = require('os').cpus().length;

  for (let i = 0; i `<` numCPUs; i++) {
    const worker = cluster.fork(); // 创建子进程

    // 监听子进程发来的消息
    worker.on('message', (message) => {
      console.log(`主进程收到消息:${message}`);
    });
  }

} else {
  // 子进程的代码

  // 工作进程可以共享任何 TCP 连接。
  // 在本例子中,它是一个 HTTP 服务器。
  http.createServer((req, res) => {
    res.writeHead(200);
    res.end('hello world\n');

    // 向主进程发送消息
    process.send('工作进程已经处理请求');
  }).listen(8000);
}

在这个例子中,如果我们的服务器运行在拥有多个 CPU 核心的机器上,我们将会对每个核心启动一个子进程。每个子进程都独立地监听同一个端口(8000),准备接收和处理进来的 HTTP 请求。当子进程处理完请求后,它会向主进程发送一个消息,告知主进程它已经完成了任务。主进程通过监听 'message' 事件来接收这些消息。

Event: 'online'open in new window

Node.js 的 cluster 模块允许你轻松创建子进程(也称为工作进程),这些子进程可以同时运行在同一台服务器上的不同核心中,使得你能够更有效地利用多核系统的资源。当你设置了一个 Node.js 应用程序来使用 cluster 模块时,你可以创建一个主进程(master process)和多个工作进程(worker processes)。

事件 'online' 是 cluster 模块中的一个事件,它在一个工作进程被创建后并且已经可以接收消息或任务时触发。当你监听这个事件时,你能够知道哪个工作进程已经启动并准备好了。

让我们通过一个简单的例子来看看如何使用这个事件:

const cluster = require('cluster');
const http = require('http');
const numCPUs = require('os').cpus().length; // 获取 CPU 核心数

if (cluster.isMaster) {
  console.log(`主进程 ${process.pid} 正在运行`);

  // Fork 工作进程
  for (let i = 0; i `<` numCPUs; i++) {
    cluster.fork();
  }

  cluster.on('online', (worker) => {
    // 这个回调会在工作进程运行起来后执行
    console.log(`工作进程 ${worker.process.pid} 已上线`);
  });
} else {
  // 工作进程可以共享任何 TCP 连接
  // 在这个案例中,它是一个 HTTP 服务器
  http.createServer((req, res) => {
    res.writeHead(200);
    res.end('你好世界\n');
  }).listen(8000);

  console.log(`工作进程 ${process.pid} 启动`);
}

在这个例子中,如果你的电脑有 4 个 CPU 核心,那么将会创建 4 个工作进程。主进程负责创建工作进程,并通过 cluster.on('online', callback) 来监听每个工作进程何时准备就绪。工作进程启动后,它们各自独立地开始监听在端口 8000 上的 HTTP 请求,任何一个工作进程都可以处理进入的 HTTP 请求。

当一个新的工作进程被成功创建并且可以接受任务时,主进程中注册的 'online' 事件监听器就会被调用,输出信息告诉你哪个工作进程现在已经处于在线状态。

这种模式能够让你的应用程序更好地利用多核心服务器的能力,提高性能,因为你可以有多个进程同时处理请求,而不是传统的单核心单线程模式。

Event: 'setup'open in new window

Node.js 是一个基于 Chrome V8 引擎的 JavaScript 运行环境,允许你在服务器端运行 JavaScript 代码。它非常适合构建快速的、可扩展的网络应用程序。

Node.js 中的 cluster 模块允许你轻松地创建子进程来利用多核 CPU 系统的性能,这些子进程可以共享同一个服务器端口。

cluster 模块中,有一个名为 'setup' 的事件。当你配置 cluster 设置(但在启动任何 worker 进程之前)时,该事件会被触发。现在,我将通俗地解释这个事件,并给出一些实际的例子。

'setup' 事件

当你使用 cluster 模块的 setupMaster() 方法(或者通过在创建子进程之前设置 cluster.settings 属性)来配置集群的一些设置时,'setup' 事件会在内部被触发。这意味着在你实际开始生成 worker 子进程之前,你已经完成了对 cluster 行为的设置。

实际例子

示例 1: 使用 setupMaster() 配置集群

const cluster = require("cluster");
const http = require("http");

if (cluster.isMaster) {
  // 监听 'setup' 事件
  cluster.on("setup", () => {
    console.log("Cluster settings configured!");
  });

  // 配置集群的工作进程默认设置
  cluster.setupMaster({
    exec: "worker.js", // 指定 worker 文件
    args: ["--use", "https"], // 传递给 worker 的参数
    silent: true, // 不要在主进程中输出 worker 的日志
  });

  // 创建工作进程
  cluster.fork();
} else {
  // Worker 进程的代码
  http
    .createServer((req, res) => {
      res.writeHead(200);
      res.end("Hello World\n");
    })
    .listen(8000);
}

在这个例子里,首先,我们引入了 clusterhttp 模块。然后检查当前脚本是在 master 进程中运行还是在 worker 进程中运行。如果是在 master 进程中,我们监听 setup 事件并且调用 cluster.setupMaster() 来配置工作进程。配置完成后,“Cluster settings configured!” 消息将会打印到控制台。接着我们调用 cluster.fork() 创建一个工作进程。

如果代码是在 worker 进程中运行的,那么我们会创建一个 HTTP 服务器,监听 8000 端口。

示例 2: 设置 cluster.settings 并触发 'setup' 事件

const cluster = require("cluster");

if (cluster.isMaster) {
  // 监听 'setup' 事件
  cluster.on("setup", () => {
    console.log(
      `Setup done with settings: ${JSON.stringify(cluster.settings)}`
    );
  });

  // 直接设置 cluster 设置而不是通过 setupMaster()
  cluster.settings = {
    exec: "worker.js",
    // 更多定制设置可以在这里添加
  };

  // 'setup' 事件将在此处隐式触发

  cluster.fork(); // 现在创建工作进程
} else {
  // ... worker 进程的代码 ...
}

在这个例子中,我们直接修改了 cluster.settings 对象来配置集群。这会隐式地触发 'setup' 事件,并且相关的监听函数将会执行。之后我们像往常一样创建工作进程。

注意,无论是在第一个示例中显式调用 setupMaster() 还是在第二个示例中直接赋值 cluster.settings,一旦调用了 cluster.fork(),就会基于这些设置来启动新的 worker 进程。

希望上述解释和例子有助于理解 Node.js v21.7.1 中的 cluster 模块 setup 事件。

cluster.disconnect([callback])open in new window

Node.js 中的 cluster 模块允许你轻松创建共享同一服务器端口的子进程。这是用来提升性能和实现在多核 CPU 上的负载均衡的一种方法。

当你使用 cluster 模块时,有一个主进程(master process),它可以根据需要生成多个子进程(worker processes)。每个子进程都是 Node.js 实例的副本,可以并行处理任务。

cluster.disconnect([callback])

cluster.disconnect 方法是在主进程中调用的,用于优雅地关闭所有的工作进程(workers)。这意味着它不会立即杀死工作进程,而是等待这些进程完成当前的工作,并且不再接受新的工作。

可选的 callback 参数是一个函数,在所有的工作进程都已经断开连接后,该函数会被调用。

例子:

假设你有一个使用 cluster 模块的 HTTP 服务器,并且你想在服务器负载低时或者进行系统维护时平滑地停止所有工作进程。

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();
    }

    // 当收到信号时,开始断开所有工作进程的连接
    process.on('SIGTERM', () => {
        console.log('主进程收到停止信号。正在断开所有工作进程...');

        cluster.disconnect(() => {
            console.log('所有工作进程已断开连接。可以安全地关闭主进程了。');
            process.exit(0);
        });
    });

} else {
    // 工作进程分享服务器端口
    http.createServer((req, res) => {
        res.writeHead(200);
        res.end('hello world\n');
    }).listen(8000);

    console.log(`工作进程 ${process.pid} 已启动`);
}

在这个例子中:

  • 主进程通过 fork() 方法衍生出多个工作进程。
  • 每个工作进程监听相同的端口(8000)上的 HTTP 请求。
  • 当主进程接收到 SIGTERM 信号时(比如你可以通过运行 kill -TERM [主进程ID] 发送该信号),它会调用 cluster.disconnect 方法。
  • 所有现存的连接将被工作进程处理完毕后关闭,不再接受新的连接。
  • 当所有工作进程都断开连接后,回调函数被执行,输出提示信息,并且主进程退出。

这样确保了服务器在关闭过程中,正在处理的请求能够顺利完成,新的请求则会由其他的服务器实例(如果有的话)处理,从而实现平滑重启或停机。

cluster.fork([env])open in new window

Node.js 中的cluster模块允许你可以轻松地创建子进程,这些子进程能够运行相同的服务器代码并共享同一端口。cluster.fork([env])是此模块中一个非常重要的函数,它用于创建新的工作进程。

在理解cluster.fork()之前,我们首先需要了解几个基本概念:

  1. Node.js 是单线程的:这意味着默认情况下,一个 Node.js 应用只会使用单个 CPU 核心。

  2. 多核 CPU:现代计算机通常都有多核 CPU,这意味着它们可以同时执行多个任务。

  3. Cluster 模块:为了让 Node.js 应用程序更好地利用多核 CPU,Node.js 提供了 Cluster 模块,它可以启动多个进程来处理负载。

  4. Master 和 Worker 进程:使用 Cluster 模块时,有一个主进程(Master)和多个工作进程(Worker)。主进程负责管理工作进程,并且工作进程实际上处理客户端请求。

现在,让我们详细了解cluster.fork([env])

cluster.fork([env])

  • cluster.fork方法用于从主进程中创建一个工作进程。每次调用fork将创建一个新的工作进程。
  • [env]参数是可选的,它允许你为新的工作进程指定环境变量。如果不提供,则工作进程会继承主进程的环境变量。

实际例子

假设你有一个 Node.js web 服务器,它在一个核心上运行很好。但随着用户数量的增加,服务器开始变得缓慢。你可以使用 Cluster 模块来改善性能。以下是一个如何使用cluster.fork的例子:

const cluster = require('cluster');
const http = require('http');
const numCPUs = require('os').cpus().length; // 获取CPU核心数

if (cluster.isMaster) {
  console.log(`Master ${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(8000);

  console.log(`Worker ${process.pid} started`);
}

在这个例子中:

  • 使用require('cluster')导入 Cluster 模块。
  • 检查当前进程是否为 master 进程。
  • 如果是,主进程会根据 CPU 核心数,通过调用cluster.fork()多次来创建相应数量的工作进程。
  • 每个工作进程都会监听相同的端口(在这里是 8000),这样就能在不同的核心上并行处理请求。
  • 如果一个工作进程死亡(因为错误或其他原因),就会触发cluster.on('exit', ...)事件,在这里你可以记录日志或者重启一个新的工作进程。

总结一下,使用cluster.fork可以让你的应用程序跨多个核心运行,提高了性能和吞吐量。这是构建能够处理大量并发请求的高效率 Node.js 应用程序的关键所在。

cluster.isMasteropen in new window

好的,让我们来聊一下 Node.js 中的 cluster 模块,特别是其中的 cluster.isMaster 属性。

首先,Node.js 是一个基于 Chrome 的 V8 JavaScript 引擎的 JavaScript 运行时环境。它通常用于构建后端服务,也就是通常所说的 API(应用程序编程接口)。Node.js 非常高效,原因之一是它使用了非阻塞的事件驱动架构。然而,由于 JavaScript 是单线程的,传统上 Node.js 只能在单个 CPU 核心上运行。这意味着即使你的服务器有多个核心,Node.js 默认情况下只会使用一个。

为了克服这一限制和更好地利用多核心系统,Node.js 提供了 cluster 模块。cluster 模块可以帮助您创建子进程(称为工作进程),这些进程运行同样的 Node.js 应用程序的副本。这样可以让不同的用户请求被分散到多个 CPU 核心上处理,从而提升性能。

这里就出现了 cluster.isMaster 属性的概念。当您启动一个 Node.js 程序时,可以通过 cluster.isMaster 来判断当前执行的进程是否是主进程(master)。如果是,通常您会用它去生成工作进程(worker),如果不是,那么就意味着当前执行的代码是在一个工作进程中运行的。

下面是 cluster.isMaster 的简单实用例子:

const cluster = require('cluster');
const http = require('http');
const numCPUs = require('os').cpus().length; // 获取 CPU 的核心数

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} 已启动`);
}

在这段代码中,程序开始执行时会判断 cluster.isMaster 是否为 true。如果为真,它表示当前的进程是主进程。在这种情况下,代码将循环 CPU 的核心数,并为每个核心调用 cluster.fork() 方法创建工作进程。每个工作进程都是应用程序完整的新实例。

如果 cluster.isMasterfalse,则意味着当前进程是由 cluster.fork() 创建的工作进程。在这些工作进程中,我们创建一个简单的 HTTP 服务器,监听 8000 端口,任何发送到这个端口的 HTTP 请求都将返回 '你好世界'。

使用了 cluster 模块后,Node.js 应用程序可以更好地利用多核服务器,以提高其处理大量并发客户端连接的能力。

cluster.isPrimaryopen in new window

Node.js 的 cluster 模块允许你轻松创建子进程来利用多核系统的能力。这些子进程可以同时运行你的 Node.js 应用的不同实例,使得应用可以处理更多的负载。

cluster 模块中,有两种类型的进程:主进程(Primary 或 Master)和工作进程(Worker)。

  • 主进程负责协调和管理工作进程,例如分发请求给工作进程、重启崩溃的工作进程等。
  • 工作进程则是实际执行具体任务的进程,比如处理网络请求。

cluster.isPrimary

cluster.isPrimary 是一个布尔值属性,用于判断当前进程是否为主进程。在 Node.js 10.0.0 之前,这个属性被称为 cluster.isMaster

如果 cluster.isPrimarytrue,则意味着当前代码运行在主进程中;如果是 false,则意味着代码运行在工作进程中。

实际使用场景

假设你正在创建一个 web 服务器,并希望它能够处理大量并发请求。使用 cluster 模块,你可以在一个主进程中创建多个工作进程,每个工作进程都有自己的事件循环和内存空间,从而并行处理请求。

下面是一个使用 cluster.isPrimary 的例子:

const cluster = require('cluster');
const http = require('http');
const numCPUs = require('os').cpus().length; // 获取 CPU 核心数

if (cluster.isPrimary) {
  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} 已启动`);
}

在这个例子中,当你运行此脚本时,主进程会首先检查 cluster.isPrimary 是否为真。如果是,它就会知道自己是主进程,并开始循环创建所需数量的工作进程。每个工作进程其实都是脚本的新实例,但 cluster.isPrimary 将会返回 false,因为它们不是主进程。然后,每个工作进程可以独立地运行 HTTP 服务器,监听端口 8000 上的请求。

当你向服务器发送请求时,主进程会将接收到的连接分配给其中一个工作进程去处理。这样的模式提高了应用程序处理并发连接的能力,尤其是在多核 CPU 系统上。

cluster.isWorkeropen in new window

Node.js 中的cluster模块允许你轻松地创建子进程来运行同一服务器的不同实例,这通常用于在多核 CPU 系统上提高性能。当你启动一个 Node.js 应用程序时,默认情况下它只会运行在单个 CPU 核心上。使用cluster模块,你可以启动一个主进程(也被称作 master 或 parent 进程),它可以生成多个工作进程(也叫 worker 或 child 进程),每个都在自己的 CPU 核心上运行同样的 Node.js 应用程序代码。

cluster模块中,有两个非常重要的属性:cluster.isMastercluster.isWorker

  • cluster.isMaster:当代码运行在主进程中时,此属性为true
  • cluster.isWorker:当代码运行在工作进程中时,此属性为true

现在,我们来聚焦于cluster.isWorker

当你检查cluster.isWorker时,如果结果为true,这就意味着当前的代码正在一个工作进程(worker process)中执行。这可以帮助你确定哪部分代码应该仅在工作进程中运行,而不是在主进程中。

下面是一个简单的例子来说明如何使用cluster.isWorker

const cluster = require('cluster');
const http = require('http');

if (cluster.isMaster) {
    // 这段代码将在主进程中运行

    // 创建工作进程
    const numCPUs = require('os').cpus().length;
    for (let i = 0; i `<` numCPUs; i++) {
        cluster.fork();
    }
} else {
    // 这段代码将在每个工作进程中运行

    // 工作进程可以共享任何TCP连接。
    // 在本例中,它是一个 HTTP 服务器
    http.createServer((req, res) => {
        res.writeHead(200);
        res.end('Hello World\n');
    }).listen(8000);
}

在上面的例子中,如果当前进程是主进程,我们会根据系统的 CPU 核心数量创建相应数量的工作进程。如果当前进程是工作进程(cluster.isWorker === true),则每个工作进程都会创建并监听自己的 HTTP 服务器实例。

这种方法可以让你的 Node.js 应用程序更好地利用多核处理器的能力,因为每个核心上都运行了应用程序的实例,能够独立处理进入的网络请求,从而提升整体性能。

cluster.schedulingPolicyopen in new window

Node.js 中的 cluster 模块允许你简单地创建子进程来处理服务器端的负载。这些子进程可以共享服务器端口并进行任务处理,从而提高应用程序的性能和吞吐量。

cluster.schedulingPolicy 是 Node.js 中一个配置项,它决定了当新的网络连接到达时,如何在多个工作进程(worker processes)之间分配这些连接。主要有两种调度策略:

  1. Round-Robin(轮询)
  2. None(操作系统默认)

Round-Robin(轮询)

在 Round-Robin 调度中,主进程接收新的连接,并将它们依次分配给每个工作进程。这样做可以确保所有的工作进程都被平等地考虑,从而尽可能公平地分配负载。

Round-Robin 模式的优点是它比较简单,且通常可以在不同工作进程之间公平地分配连接。

实际例子: 假设你有一个 Node.js 应用,运行在 4 个核心的 CPU 上,你想利用所有的核心来处理 HTTP 请求。使用 cluster 模块,你可以创建一个主进程和 3 个工作进程,每个工作进程监听相同的端口。使用 Round-Robin 策略,当 HTTP 请求到达时,主进程会把第一个请求发给第一个工作进程,第二个请求发给第二个工作进程,以此类推,直到所有工作进程都获得了请求,然后从头开始。

None(操作系统默认)

如果不使用 Round-Robin 策略,Node.js 会使用操作系统的默认策略来分配网络连接。在这种情况下,工作进程直接接受新的连接,操作系统负责负载平衡。

这种默认策略的优点是,它依赖于操作系统更底层的机制来分配连接,通常这样会更高效。

实际例子: 继续上面的例子,如果我们没有设置 Round-Robin ,那么新的 HTTP 请求就由操作系统来决定它应该被哪个工作进程处理。这意味着,如果某个工作进程很忙,而其他的不那么忙,操作系统可能会选择一个当前不太忙的工作进程来处理新的连接。

如何设置

cluster.schedulingPolicy 可以通过环境变量或者在代码中直接设置。环境变量 NODE_CLUSTER_SCHED_POLICY 可以设置为 rr (表示 Round-Robin) 或 none (表示使用操作系统默认策略)。

// 在代码中设置
const cluster = require("cluster");

if (cluster.isMaster) {
  // 强制使用 Round-Robin 策略
  cluster.schedulingPolicy = cluster.SCHED_RR;
  // 创建工作进程 ...
} else {
  // 工作进程代码 ...
}

或者在启动应用程序时设置环境变量:

NODE_CLUSTER_SCHED_POLICY=rr node app.js

选择哪种调度策略取决于你的应用需求和部署环境。对于某些场景,轮询可能会带来更加均匀的负载分布,而在其他情况下,操作系统的默认策略可能会提供更好的性能。

cluster.settingsopen in new window

Node.js 中的 cluster 模块允许你轻松创建共享同一服务器端口的子进程。这对于在多核 CPU 系统上扩展网络应用非常有用,因为它可以帮助你充分利用多核系统的计算资源。

当你使用 cluster 模块启动一个主进程时,你可以设定一些初始配置,这些配置将会被存储在 cluster.settings 对象中。这个对象包含了一系列属性,比如 exec, args, silent 等等,这些属性记录了集群是如何被启动的。

下面,我将通过一个例子来展示如何使用 cluster 模块,并解释 cluster.settings 的作用。

假设我们想创建一个简单的 HTTP 服务器,并且想要在多核 CPU 上运行多个节点(Node.js 的实例)以提高性能。

首先,我们需要编写一个 JavaScript 文件,例如叫做 server.js

const cluster = require('cluster');
const http = require('http');
const numCPUs = require('os').cpus().length; // 获取CPU的核心数

if (cluster.isMaster) {
  // 这部分是主进程执行的代码

  console.log(`主进程 ${process.pid} 正在运行`);

  // 创建工作进程(worker)
  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} 已启动`);
}

在上面的例子中,如果你运行 server.js 文件,主进程会根据你的 CPU 核心数创建对应数量的工作进程,每个工作进程都监听相同的端口(8000)。客户端发送的请求可以由任意一个工作进程处理。

关于 cluster.settings 的实际应用:

在上述代码中,我们没有显式地设置 cluster.settings 中的任何选项。但是,我们可以根据需要进行配置。例如,我们可以指定工作进程运行的脚本、工作进程的参数等。

// 假设我们有一个 'worker.js' 脚本专门用于工作进程
const clusterSettings = {
  exec: "worker.js",
  args: ["--use", "https"],
  silent: true,
};

if (cluster.isMaster) {
  cluster.setupMaster(clusterSettings);
  // ... 其他的 master 代码 ...
}

// ... rest of the code remains the same ...

在这个新的例子中,我们传递了一个配置对象给 setupMaster() 方法,它定义了要执行的工作进程的脚本(worker.js),传递给工作进程的参数(['--use', 'https']),以及是否要静默输出(silent: true 不会把工作进程的输出打印到控制台)。

所有这些配置信息都会被存储在 cluster.settings 对象中,所以之后你可以随时查看或者修改这些配置。

console.log(cluster.settings.exec); // 输出 'worker.js'
// ... 可以基于 cluster.settings 做更多事情 ...

记住,cluster.settings 只影响通过调用 cluster.setupMaster() 设置的值,或者是在初始化集群时隐式设置的默认值。这个对象不会反映任何在工作进程中发生的变化。

cluster.setupMaster([settings])open in new window

Node.js 的 cluster 模块允许你简单地创建共享服务器端口的子进程。这是一种在单个机器上利用多核 CPU 优势的方法,可以让 Node.js 应用更好地进行并行处理,提高性能。

cluster.setupMaster([settings])cluster 模块中的一个方法,用来配置创建子进程(也叫工作进程或 workers)时的一些默认设置。当你调用 cluster.fork() 创建新的工作进程时,这些设置将被使用。

下面是 cluster.setupMaster 方法的参数说明:

  • settings:它是一个对象,可以包含以下属性:
    • exec: 用于指定工作进程所运行的脚本文件路径。
    • args: 设置工作进程启动时传递给脚本的命令行参数数组。
    • silent: 布尔值,当设置为 true 时,工作进程的输出不会发送到父进程。
    • 其他选项,如 cwd(工作目录)、env(环境变量)等。

接下来,我会给出一个实际应用的例子,来展示如何使用 cluster.setupMaster 方法。

实际例子:

假设你正在编写一个 Node.js 的网络服务器,想要充分利用多核 CPU。你决定使用 cluster 模块来创建多个工作进程,每个工作进程都运行相同的服务器代码。

首先,创建一个名为 server.js 的文件,里面有你的服务器代码:

// server.js 文件内容
const http = require("http");

http
  .createServer((req, res) => {
    res.writeHead(200);
    res.end("Hello World!\n");
  })
  .listen(8000);

console.log(`Worker ${process.pid} started`);

然后,创建另一个名为 master.js 的文件,该文件将使用 cluster 模块来管理工作进程:

// master.js 文件内容
const cluster = require('cluster');
const numCPUs = require('os').cpus().length; // 获取CPU的核心数

if (cluster.isMaster) {
  console.log(`Master ${process.pid} is running`);

  // 设置工作进程的配置
  cluster.setupMaster({
    exec: 'server.js', // 指向工作进程要执行的文件
    args: [], // 可以传递任何需要的命令行参数
    silent: false // 工作进程的标准输入输出会被发送到主进程
  });

  // 根据CPU核心数创建相应数量的工作进程
  for (let i = 0; i `<` numCPUs; i++) {
    cluster.fork();
  }

  cluster.on('exit', (worker, code, signal) => {
    console.log(`Worker ${worker.process.pid} died`);
    cluster.fork(); // 可以选择重新启动一个新的工作进程
  });
} else {
  // 如果进程不是主进程,则应该是通过 .fork() 创建的工作进程,它们会执行上面设置的 `exec` 脚本
}

在这个例子中,如果你运行 master.js,Node.js 会启动一个主进程,并根据你电脑的 CPU 核心数量创建多个工作进程,每个工作进程都运行 server.js 文件的内容,并且监听同一个端口(8000)。这样,你的服务器就能够同时处理更多的请求,从而提高应用的整体性能。

cluster.setupPrimary([settings])open in new window

cluster.setupPrimary([settings]) 是 Node.js 中一个用于设置集群模块主进程的函数。在我们深入之前,先来理解一下 Node.js 中的集群(Cluster)是什么。

Node.js 通常会在单个进程中运行,这意味着它默认只使用一颗 CPU 核心。然而,现代服务器通常有多个核心。为了让 Node.js 应用能够更好地利用多核系统的性能,我们可以使用 Node.js 的 cluster 模块来创建一个由一个主进程(master process)和多个工作进程(worker processes)组成的集群。这样,每个工作进程都可以在自己的 CPU 核心上独立运行,从而提高应用的并发处理能力。

现在,来看 cluster.setupPrimary([settings]) 这个函数。

功能

cluster.setupPrimary() 函数允许你在集群的主进程启动之前修改或配置某些属性。调用这个函数可以指定如何创建新的工作进程,包括它们使用的执行脚本、环境变量以及其他重要的行为设定。

参数

  • settings: 可选参数,是一个对象,其中可以包含以下属性:
    • execArgv: 主进程传给 Node.js 解释器的参数列表,通常是命令行参数。
    • exec: 要执行的脚本或模块路径,即新的工作进程启动时将运行的文件。
    • args: 传递给工作进程的参数列表。
    • silent: 布尔值,如果设置为 true,则工作进程的 stdin、stdout 和 stderr 将会被重定向到主进程,否则它们将从父进程继承这些流。
    • ...其他属性。

返回值

该函数返回 cluster 模块本身,因此可以链式调用其他 cluster 相关的函数。

实际运用例子

假设你正在构建一个 Node.js Web 服务器,希望它能够在多核 CPU 上运行以提高性能。你可能会采取如下步骤来配置和启动集群:

const cluster = require('cluster');
const http = require('http');
const numCPUs = require('os').cpus().length;

if (cluster.isPrimary) {
  // 主进程代码:设置工作进程

  // 配置工作进程要运行的脚本和参数
  cluster.setupPrimary({
    exec: 'app.js', // 工作进程运行的文件
    args: ['--use', 'https'], // 传递给工作进程的参数
    silent: false // 工作进程的标准输入输出不重定向到主进程
  });

  // 根据 CPU 的数量来启动相同数量的工作进程
  for (let i = 0; i `<` numCPUs; i++) {
    cluster.fork();
  }

  cluster.on('exit', (worker, code, signal) => {
    console.log(`工作进程 ${worker.process.pid} 已退出`);
  });

} else {
  // 工作进程代码:创建 HTTP 服务器
  http.createServer((req, res) => {
    res.writeHead(200);
    res.end('hello world\n');
  }).listen(8000);

  console.log(`工作进程 ${process.pid} 已启动`);
}

在上面的代码中,我们首先通过 require('cluster') 引入了 Node.js 的 cluster 模块。然后检查当前是否在主进程中运行(通过检查 cluster.isPrimary)。如果是,在主进程中我们调用了 cluster.setupPrimary() 设置了工作进程的相关参数。接着,我们根据系统的 CPU 核心数目启动了相应数量的工作进程(通过 cluster.fork())。

每个工作进程都会运行 app.js 文件,并监听端口 8000 上的 HTTP 请求。当工作进程退出时,主进程会收到 'exit' 事件的通知。

这种方式可以让 Node.js 应用在多核服务器上实现更高的吞吐率和更好的负载均衡。

cluster.workeropen in new window

cluster.worker 是 Node.js 中 cluster 模块的一个属性,它提供了对当前工作进程(worker)对象的引用。在 Node.js 中,cluster 模块允许你创建一组工作进程,这些进程共享同一个服务器端口,并且能够并行处理任务,这对于充分利用多核 CPU 性能非常有帮助。

当你在主进程中使用 cluster.fork() 方法创建新的工作进程时,每个工作进程都是一个独立的 Node.js 进程。在工作进程内部,你可以通过 cluster.worker 访问该进程的详细信息,例如进程的 ID、状态等。

下面我会给你举几个实际应用的例子来说明 cluster.worker 是如何使用的:

实例 1:获取工作进程 ID

const cluster = require('cluster');
const http = require('http');

if (cluster.isMaster) {
    // 这里是主进程代码
    console.log(`主进程 ${process.pid} 正在运行`);

    // 创建工作进程
    for (let i = 0; i `<` 2; i++) {
        cluster.fork();
    }
} else {
    // 这里是工作进程代码
    // 使用 cluster.worker 来获取当前工作进程的ID
    console.log(`工作进程 ${cluster.worker.id} 启动`);

    // 工作进程可以共享任何TCP连接。
    // 在本例中,它是一个HTTP服务器。
    http.createServer((req, res) => {
        res.writeHead(200);
        res.end(`你好,这条消息来自工作进程 ${cluster.worker.id}\n`);
    }).listen(8000);
}

在上面的例子中,我们首先判断是否为主进程(cluster.isMaster),如果是,则打印主进程的 PID 并创建两个工作进程。在工作进程内部,我们通过打印 cluster.worker.id 来获取每个工作进程的 ID,并启动一个 HTTP 服务器监听在端口 8000 上。这样,当 HTTP 请求到达时,客户端将会看到是哪个工作进程在处理他们的请求。

实例 2:优雅地退出工作进程

const cluster = require("cluster");

if (cluster.isMaster) {
  // 主进程代码
  const worker = cluster.fork();

  setTimeout(() => {
    // 发送关闭消息给工作进程
    worker.send("shutdown");
    // 断开与工作进程的连接
    worker.disconnect();
    setTimeout(() => {
      if (!worker.isDead()) {
        // 如果工作进程没有正常退出,则强制杀掉
        worker.kill();
      }
    }, 2000);
  }, 5000);
} else {
  // 工作进程代码
  process.on("message", (msg) => {
    if (msg === "shutdown") {
      // 清理工作
      console.log(`工作进程 ${cluster.worker.id} 正在关闭`);
      // 关闭所有的服务器和定时器等
      // 开始优雅的关闭过程...
    }
  });
}

在这个例子中,主进程先创建了一个工作进程,然后通过 setTimeout 设置了一个 5 秒后执行的延迟任务来发送 'shutdown' 信息给工作进程。工作进程收到消息后开始进行清理操作,比如关闭服务器和定时器等,以便优雅地退出。主进程接着调用 worker.disconnect() 来断开与工作进程的连接,并设置另一个 2 秒的延迟任务来检查工作进程是否已经退出,如果没有,则使用 worker.kill() 强制终止它。

这样的 cluster.worker 对象使用案例能够帮助你在编写可扩展的 Node.js 应用程序时更好地管理工作进程。

cluster.workersopen in new window

Node.js 的 cluster 模块允许你容易地创建共享同一个服务器端口的子进程,这被称为工作进程(worker processes)。这样做的目的是为了充分利用多核 CPU 的能力,因为 Node.js 实例运行在单个线程中。如果你有一个四核的 CPU,你可以启动四个 Node.js 进程,每个进程都在各自的核上运行,以此来增加你的应用程序的性能和吞吐量。

cluster.workers 对象

cluster 模块中,cluster.workers 是一个对象,它包含了所有活跃的工作进程。键是工作进程的 ID,值是工作进程对象本身。你可以使用这个对象来获取和控制集群中的每一个工作进程。

举一个简单的例子来说明如何使用 cluster.workers

const cluster = require('cluster');
const os = require('os');

if (cluster.isMaster) {
  // 这段代码将在主进程中运行

  const numCPUs = os.cpus().length; // 获取 CPU 的核心数
  console.log(`主进程 ${process.pid} 正在运行`);

  // 衍生工作进程。
  for (let i = 0; i `<` numCPUs; i++) {
    cluster.fork();
  }

  // 当工作进程被衍生时,打印它们的 PID。
  cluster.on('online', (worker) => {
    console.log(`工作进程 ${worker.process.pid} 已启动`);
  });

  // 如果工作进程退出,就重新启动一个新的工作进程。
  cluster.on('exit', (worker, code, signal) => {
    console.log(`工作进程 ${worker.process.pid} 已退出`);
    cluster.fork();
  });

} else {
  // 工作进程的代码将在这里运行
  require('./app'); // 假设 'app' 是你的服务器代码
}

// 在主进程中,你可以通过下面的方式遍历所有工作进程:
if (cluster.isMaster) {
  for (const id in cluster.workers) {
    const worker = cluster.workers[id];
    console.log(`工作进程 ID: ${id}, PID: ${worker.process.pid}`);
  }
}

在这个例子中,我们先检查当前进程是否是主进程 (cluster.isMaster)。如果是,我们就根据系统的 CPU 核心数来决定创建多少个工作进程。然后,我们监听了两个事件:onlineexit。当一个工作进程启动并在线时,会触发 online 事件;当工作进程停止时,会触发 exit 事件,在这个事件的回调中我们又调用了 cluster.fork() 来重新启动一个新的工作进程来替代已经停止的那个。

最后,我们在主进程中使用 for-in 循环来遍历 cluster.workers 对象,并得到每个工作进程的 ID 和 PID。这可以用于监控或管理每个工作进程的状态。

在实际的服务器运行环境中,利用 cluster 模块可以显著提高 Node.js 应用的性能,特别是对于需要处理大量并发连接的网络服务十分有效。通过在多个 CPU 核心上分散负载,可以实现更好的资源利用率和更快的响应时间。