在节点中进行多任务处理.js使用群集模块
我们已经知道Node.js是单线程的,这意味着它将在可能具有多个内核的系统中使用处理器的单个内核。尽管它可以很好地处理单线程系统的负载,但肯定有优化的空间,集群模块是这样做的一种方式。
引入群集模块是为了通过在多个处理器内核上运行工作进程/子进程来扩展应用程序执行。这些进程共享相同的服务器端口,但即使使用相同的端口,这些进程在一天结束时也是独立的进程。因此,他们每个人都有自己的 V8 实例、事件循环、自己的内存以及所有爵士乐。这些进程使用 IPC(进程间通信)通道与父进程进行通信。在学习本教程的过程中,我们将查看所有功能,因此让我们深入研究一下。
我们试图解决的问题
让我们通过键入 来快速设置节点项目。(为了方便起见,我将添加快递,但你不一定非要这样做)。通过键入 来安装快速和负载测试。我们将在本教程的后面部分使用 loadtest 来了解使用群集模块获得的性能优势。安装后,创建一个名为 nonCluster 的文件.js并复制此代码(此文件将包含不带群集模块的代码,以便进行比较)。npm init -ynpm i express loadtest
这是一个非常简单的快速应用程序,有2个请求。终结点执行 CPU 密集型任务,从而阻止事件循环。终结点会立即返回响应。简单!/heavy/light
现在运行服务器,先向终结点发出请求,然后再向终结点发出请求。终端节点显然需要时间,但您会注意到终端节点也花费了相同的时间。这是因为端点占用了计算请求的时间,因此阻塞了事件循环。因此,在请求之后发出的任何请求现在都必须等待其完成。/heavy/light/heavy/light/heavy/heavy
问题的解决方案
因此,为了避免这种阻塞,让我们添加集群模块。在名为 cluster.js的新文件中,复制以下代码:
让我们来分解一下。最初,当您有一个进程时,它会解析所有传入的请求。现在我们使用的是群集模块,将有 2 种类型的进程。父/主进程和子进程。最初,当服务器启动时,它将启动一组进程。之后,每当有人向服务器发出请求时,父进程就会将请求定向到子进程(主要以轮循机制方式)。然后,子进程将最终解决该请求。
在块内,我们将检查当前进程是否是父进程。群集模块具有一个名为 的属性,该属性允许您知道当前进程是子进程还是父进程。如果是父进程,则使用 fork 方法创建群集。(我们只启动与系统中存在的内核总数相等的进程,以避免调度开销。
如果它不是父进程,则意味着它是一个子进程。因此,这个子进程现在实际上负责解析请求。它将具有所有API端点及其相应的逻辑。ifisMaster
分叉新工作线程后,它将以事件进行响应。我们将在父级上侦听此事件,以查看是否按预期创建了所有进程。online
当工作线程死亡时,群集模块会发出一个事件。因此,为了不使我们的应用程序停机,我们将在新进程出现故障时分叉。这样,我们将始终启动并运行一组进程,即使任何其他进程因任何有意或无意的原因而关闭。exit
好吧,这看起来不错。现在,如果运行此文件并发出相同的请求(首先向终结点发出请求,然后向终结点发出请求),则会看到它按预期工作,而不会阻塞事件循环。因此,添加一组进程确实有帮助。现在,让我们使用负载测试包来执行一些相对繁重的测试。/heavy/light
由于群集应用程序已在运行,因此让我们先对其进行测试。键入以运行测试。(如果您没有全局安装负载测试,只需在命令开头添加 npx 即可。loadtest -n 1000 -c 100
-n 表示我设置为 1000 的请求总数。
-c 代表并发。它基本上将模拟一个真实世界的环境,其中应用程序同时从多个客户端接收请求,在这种情况下,它将是100个同时客户端。
这些是“群集”应用程序的汇总结果。
现在,让我们切换到“非群集”应用程序,并再次运行相同的测试。
使用群集时,整体性能明显有显著提高。但有一个问题。这两项测试都是针对端点的,这是一个 CPU 密集型操作。让我们尝试运行相同的测试,但这次是针对终结点。/heavy/light
惊喜,惊喜。在此示例中,使用群集时,我们的性能实际上相对较差。为什么?
你看,Node.js主要是为I/O操作设计的,在大多数情况下,它不会阻塞事件循环。因此,由于它知道如何处理这些类型的操作,因此添加额外的进程并将请求路由到这些进程中的每一个最终都是很可能不需要的开销。但是,在 CPU 密集型阻塞操作的情况下,最好在确实需要群集的进程之间拆分请求数。
因此,它最终归结为您的应用程序的设计目的。如果你有一个微服务架构,并且有一个特定的服务来处理 CPU 密集型操作,你可以为该特定服务启动一个集群,其余的可以由你的单线程节点进程处理。
所以,这就是你在 Node.js应用程序中使用集群的方式。它有自己的一组警告,在将其添加到代码库之前,您需要了解它们。