首页  >  文章  >  web前端  >  集群和工作线程 - Node JS

集群和工作线程 - Node JS

Patricia Arquette
Patricia Arquette原创
2024-11-26 00:22:11438浏览

Clustering and Worker Threads - Node JS

在上一篇文章“Node JS 内部结构”中,我们讨论了 Node JS 内部架构,并讨论了为什么我们应该增加节点线程池大小以同时处理多个请求。我已经告诉过你,可扩展性和性能与线程池大小无关。

为了可扩展性和高性能,我们可以使用集群和工作线程。

聚类

假设您正在参加一场盛大的婚礼,有数千名宾客参加婚礼。有一间厨房,一名厨师正在为所有这些客人准备食物。听起来不可预测,对吧?如果您只有一名厨师,您就没有充分利用厨房的全部资源。

这正是在多核 CPU 上运行的 Node JS 应用程序中发生的情况,当仅使用一个核心来处理所有请求时。因此,即使我们的机器具有多核的能力,如果没有集群,我们的应用程序也只能在单核上运行。一个核心负责处理所有工作。

当你的厨房里有多个厨师正在工作时,这就是集群。

集群是一种使单个 Node JS 应用程序能够有效利用多个 CPU 核心的技术。

要实现集群,您必须使用 Node JS 中的集群模块。

const cluster = require('cluster');

通过使用此集群模块,您可以创建 Node JS 应用程序的多个实例。这些实例称为工人。所有工作人员共享相同的服务器端口并同时处理传入请求。

集群架构中有两种类型的进程。

1.主流程:

Master进程就像厨房里的主厨管理工人。它初始化应用程序,设置集群环境,并将任务委托给工作进程。它不直接处理应用程序请求。

Master进程是做什么的?

  • 使用 cluster.fork() 方法创建多个工作进程。如果工作人员意外崩溃或退出,它还会重新启动工作人员。

  • 它确保传入请求分布在所有工作进程中。在 Linux 上,这是由操作系统处理的,在 Windows 上,Node JS 本身充当负载均衡器。

  • 它可以通过IPC(进程间通信)在worker之间进行通信。

2.工作进程:

工作进程是主进程创建的 Node JS 应用程序的实例。每个进程在单独的 CPU 核心上独立运行并处理传入请求。

工作进程无法直接相互通信,它们通过主进程进行通信。

工作进程处理传入的请求并执行一些任务,例如数据库查询、计算或任何应用程序逻辑。

const cluster = require('cluster');

在这里,我们首先检查这是主进程。如果是,那么它将创建工作进程。

在我们的代码中,我使用 cluster.fork() 创建一个工作进程。

但是,这不是创建工作进程的理想方式。

假设您正在创建 4 个工作进程,并且您的系统有两个核心。

为了解决这个问题,而不是创建硬编码的工作进程,首先找到 CPU 核心,然后考虑数据创建工作进程。

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

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

  // Fork workers
    cluster.fork();
    cluster.fork();
    cluster.fork();
    cluster.fork();

} else {
  console.log(`Worker ${process.pid} is running`);
  // Worker logic (e.g., server setup) goes here
}

我使用双核系统,所以输出将如下所示。

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

if (cluster.isMaster) {
  console.log(`Master ${process.pid} is running`);
  const numCPUs = os.cpus().length;

  // Fork workers
  for (let i = 0; i < numCPUs; i++) {
    cluster.fork();
  }
} else {
  console.log(`Worker ${process.pid} is running`);
  // Worker logic (e.g., server setup) goes here
}

现在,您有一个问题,如果您有双核 CPU,那么为什么要创建 4 个工作进程?

这是因为逻辑核心数为 4,我的 CPU 支持超线程或同步多线程 (SMT)。

工作线程

在餐厅里,服务员接受订单并将订单交给厨师团队,因为烹饪需要一些时间。如果有清洁桌子或任何其他与服务员相关的工作,那么服务员就会这样做。当订单准备好后,厨师将食物返还给服务员,服务员将这些食物提供给顾客。

这与工作线程相关的场景相同。如果出现任何计算量大的任务,例如大规模数据处理、复杂计算或繁重算法,则主线程会将此任务委托给工作线程。该任务由工作线程执行,而不是主线程。

*为什么,这有帮助? *

我们知道 Node JS 事件循环是单线程的,如果这种繁重的计算工作由主线程完成,那么事件循环将被阻塞。如果您使用这些工作线程,那么这些繁重的任务将交给工作线程,工作线程执行这些任务,而不是主线程,因此事件循环不会被阻塞。

工作线程可以通过消息传递系统与主线程通信,并且可以使用结构化克隆(深复制)在线程之间发送数据。

现在,我们正在尝试模仿工作线程的工作。

main.js(主线程)

Master 12345 is running
Worker 12346 is running
Worker 12347 is running
Worker 12348 is running
Worker 12349 is running

worker.js(工作线程)

const { Worker } = require('worker_threads');

function startWorker() {
  const worker = new Worker('./worker.js'); // Create a worker using worker.js

  // Listen for messages from the worker
  worker.on('message', (message) => {
    console.log('Message from worker:', message);
  });

  // Handle errors in the worker
  worker.on('error', (error) => {
    console.error('Worker error:', error);
  });

  // Handle worker exit
  worker.on('exit', (code) => {
    console.log(`Worker exited with code ${code}`);
  });

  // Send a message to the worker
  worker.postMessage({ num: 100 });
}

startWorker();

如果数据包含大型结构,它将被深度克隆并传递,这可能会产生一些性能开销。

代码的工作

  • Worker 类用于生成新线程。

  • 您可以使用worker.postMessage向worker发送数据,并使用worker.on('message',callback)监听消息。

  • 在工作线程中,parentPort 是与主线程通信的主要接口。

  • 您可以监听来自主线程(parentPort.on('message'))的消息并使用parentPort.postMessage发回消息。

输出将是:

const cluster = require('cluster');

现在,您还有一个问题:为什么我们不创建数百个工作线程?

但是,原因是如果您创建的线程多于核心数量,线程将竞争 CPU 时间,导致上下文切换,这会导致成本高昂并降低整体性能。

什么时候应该在 Node.js 中使用集群、工作线程或两者?

1。何时使用工作线程?

  • CPU 密集型任务:

涉及大量计算的任务,例如图像/视频处理、数据压缩或加密、机器学习推理、科学计算

  • 需要共享内存:

您需要在线程之间有效地共享数据而不重复数据。

  • 单核使用情况:

如果您的应用程序只需要在单个进程内扩展,但仍然需要 CPU 密集型任务的并行性。

2.什么时候使用聚类?

  • I/O 限制:

任务涉及处理大量客户端请求,例如 Web、服务器、聊天应用程序和 API。集群通过在所有 CPU 核心之间分配请求来帮助水平扩展。

  • 孤立的记忆:

您的应用程序不需要在进程之间共享大量数据。

  • 多核利用率:

您希望通过生成多个 Node.js 进程来利用所有可用的核心。

3.什么时候同时使用集群和工作线程?

  • I/O 密集型 CPU 密集型任务:

应用程序处理 HTTP 请求,但卸载计算密集型任务。示例:网络服务器处理文件上传并执行图像大小调整或视频转码。

  • 高可扩展性:

您需要进程级和线程级并行性才能实现高吞吐量。在电子商务网站中,集群可确保多个进程处理传入请求。工作线程处理后台任务,例如生成个性化推荐。

谢谢。

欢迎提出问题或提出任何建议。

如果您发现本文内容丰富,请点赞。

以上是集群和工作线程 - Node JS的详细内容。更多信息请关注PHP中文网其他相关文章!

声明:
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn