首页 > 解决方案 > 是什么导致node.js处理移除多线程状态的阻塞状态?

问题描述

我正在尝试制作一个“多线程”快速服务器,它将产生一个执行任务的进程。

这背后的整个想法是重构我的高强度单线程 node.js 应用程序,以便我可以生成更多进程,从而同时运行单独的密集任务。

// Primary app (master).
// File called: app.js
const express = require('express');
const { fork } = require('child_process');
const app = express();

var counter = 0;
app.post('/intensiveTask', async (req, res) => {
    try {
        const forked = fork('./child.js');
        console.log(`forked child: ${++counter}`)
        forked.send(counter);
        forked.on('message', (m) => {
              console.log('from child: ' + m);
              res.sendStatus(200);
              forked.kill();
        });
     } catch (err) {
        console.log(err.stack)
        res.json({ error: err.message || err.toString() });
     }
});

try {
    app.listen(8080);
} catch (err) {
    console.error(`Failed to start the server due to the following error: ${err}`);
}


// Process app (child).
// File called: child.js

process.on('message', (m) => {
    console.log('inside child: ' + m);

    var time = Math.floor(Math.random() * Math.floor(5000));
    console.log(`about to wait ${time} for process ${m}`);

    setTimeout(() => {
        process.send(`finish... ${m}`);
    }, time);

});

我遇到的主要问题与使用 JMeter 的这些结果有关:对于 50 个线程,我大约需要 16 秒。对于 100 个线程,我得到大约 32 秒。问题是它们都同时到达。我预计第一个请求会更快到来,而不是最终同时出现。所以我想知道这种阻塞状态的原因可能是什么......

In the begining block: 
...
forked child: 176
forked child: 177
forked child: 178
forked child: 179
...
In the midddle block:
...
about to wait 735 for process 159
about to wait 4475 for process 133
about to wait 518 for process 131
inside childe: 125
about to wait 3909 for process 100
...
At the end, a block of finishing state :
...
from child: finish... 84
from child: finish... 81
from child: finish... 83
from child: finish... 88
...

标签: javascriptnode.jsmultithreadingparallel-processingfork

解决方案


如果您启动 100 个进程并要求它们都执行相同数量的 CPU 密集型工作,那么您的操作系统将尝试在这 100 个进程之间尽可能平等地共享 CPU。这将导致所有 100 个进程大约在同一时间完成。

此外,与 CPU 中的内核相比,整个系统启动更多都在执行 CPU 密集型工作的工作进程并不更有效,因为这只会为操作系统在它们之间不断进行时间分片创造额外的开销这会降低整体吞吐量。

如果您想要的是首先到达的第一个任务(明显在后面的任务之前)和最后一个最后完成的任务,那么您想要的架构可能是一个工作队列。

您可以创建与核心数量一样多的工作进程,并为传入工作创建队列。当传入请求到达时,您会查看是否有空闲的工作人员。如果有,您将工作交给免费工人。如果没有空闲的 worker,请求进入 FIFO 队列。当其中一个工作人员完成时,您然后从队列中获取最旧的项目并将该请求传递给工作人员而不是刚刚完成。这种架构将让第一个请求更快完成并让后面的请求等待。它还可以扩展到更大的负载,因为很明显,如果您为每个新请求创建一个新进程,很快您就会拥有如此多的进程,以至于您已经消耗了服务器上的大量资源(尤其是内存)并且所有进程都在竞争彼此。

您的另一个选择是使用 node.js 中的集群模块。这将为您系统中的每个 CPU 内核创建一个进程,并且它会自动将传入请求移交给其中一个进程,包括移交实际传入的 HTTP 连接,以便工作人员甚至可以为您发送响应。

我没有特别的建议,但也有已经为 node.js 编写的作业队列模块,因此您也可以使用其中之一,而不是自己编写。


推荐阅读