在 NestJS HTTP 服务器中使用子进程时,受 CPU 限制的进程会阻塞工作池
Posted
技术标签:
【中文标题】在 NestJS HTTP 服务器中使用子进程时,受 CPU 限制的进程会阻塞工作池【英文标题】:CPU-bound process blocks worker pool while using Child Process in NestJS HTTP server 【发布时间】:2019-04-23 06:50:30 【问题描述】:节点版本: v10.13.0
我正在尝试对涉及大量 CPU 计算的 NodeJS 请求并发进行非常简单的测试。我知道 NodeJS 不是 CPU 密集型进程的最佳工具,不应该系统地生成子进程,但这段代码是为了测试子进程的工作方式。这也是用 TypeScript 编写的,使用 NestJS。
src/app.controller.ts
import Get, Param, Controller from '@nestjs/common';
import fork = require('child_process');
@Controller()
export class AppController
@Get()
async root(): Promise<string>
let promise = new Promise<string>(
(resolve, reject) =>
// spawn new child process
const process = fork.fork('./src/cpu-intensive.ts');
process.on('message', (message) =>
// when process finished, resolve
resolve( message.result);
);
process.send();
);
return await promise;
src/cpu-intensive.ts
process.on('message', async (message) =>
// simulates a 10s-long process
let now = new Date().getTime();
let waittime = 10000; // 10 seconds
while (new Date().getTime() < now + waittime) /* do nothing */ ;
// send response to master process
process.send( result: 'Process ended' );
);
这么长的过程,如果在没有产生新子进程的情况下执行,会导致这个结果时间线,有 5 个并发请求(从 #1 到 #5 注明)。每个进程阻塞循环事件,每个请求都必须等待前一个请求完成才能得到响应。
Time 0 10 20 30 40 50
#1 +----+
#2 +----+----+
#3 +----+----+----+
#4 +----+----+----+----+
#5 +----+----+----+----+----+
在生成新的子进程时,我期望每个进程将由我的 CPU 上的不同逻辑核心同时处理(我的有 8 个逻辑核心),导致这个预测的时间表:
Time 0 10 20 30 40 50
#1 +----+
#2 +----+
#3 +----+
#4 +----+
#5 +----+
虽然,我在每次测试中都观察到了这个奇怪的结果:
Time 0 10 20 30 40 50
#1 +----+
#2 +----+----+
#3 +----+----+----+
#4 +----+----+----++
#5 +----+----+----+-+
前 3 个请求就像工作池被饿死一样,尽管我假设会创建 3 个不同的池。最后的 2 个请求非常令人困惑,因为它们的行为就像与请求 #3 并行工作。
我目前正在寻找以下原因的解释:
为什么前 3 个请求不像同时运行一样 为什么最后 3 个请求表现得好像同时运行请注意,如果我添加另一个“快速”方法如下:
@Get('fast')
async fast(): Promise<string>
return 'Fast process ended.';
这种方法不受并发运行的 CPU 密集型进程的影响,并且总是立即回复。
【问题讨论】:
有任何更新吗? 您是否每次都能始终如一地获得这些结果? 据我了解,这是因为当我们调用子进程并开始执行 cpu 密集型工作时,所以当时我们的主线程正在执行同步操作,所以它无法处理来自的响应子进程,当主线程完成其工作之后,它能够处理来自子进程的响应。所有操作都在并行执行,但由于同步操作,主线程无法处理响应。继续它。我希望它有意义。对于 POC,您可以在子进程中执行一些操作 n 检查操作是否在开始时间完成。 更多细节可以阅读这个节点问题github.com/nodejs/node/issues/14917 @mihai:是的,这是一致的 【参考方案1】:我在我的机器上执行了测试用例,它工作正常,你能在你的机器上检查一下吗?
节点版本:v8.11.2 操作系统:macOs High Sierra 10.13.4, 8 Cores
child-process-test.js
const child_process = require('child_process');
for(let i=0; i<8; i++)
console.log('Start Child Process:',i,(new Date()));
let worker_process = child_process.fork("cpu-intensive-child.js", [i]);
worker_process.on('close', function (code)
console.log('End Child Process:', i , (new Date()), code);
);
cpu-intensive-child.js
const fs = require('fs');
// simulates a 10s-long process
let now = new Date().getTime();
let waittime = 10000; // 10 seconds
while (new Date().getTime() < now + waittime) /* do nothing */ ;
// send response to master process
// process.send( result: 'Process ended' );
输出
您可以在输出中检查所有过程的差异仅为10 sec
,您可以在您的机器上执行此测试用例并告诉我,可能会有所帮助。
【讨论】:
确实有效。然而这不是同一个用例,因为这里没有外部事件处理,对吧?感谢您的努力! 是的,这里没有外部事件处理。在您的用例中,您创建了一个http-server
,否则没有区别。我认为两者应该工作相同。
他们可能应该,但他们没有,不像我那样使用 NestJS。那么http服务器层上一定有什么东西会产生我描述的奇怪行为?至少您强调不是导致它的 child_process 。我会尽量减少用例(不直接使用 NestJS 种子)。以上是关于在 NestJS HTTP 服务器中使用子进程时,受 CPU 限制的进程会阻塞工作池的主要内容,如果未能解决你的问题,请参考以下文章
运行nestjs应用程序时typeorm迁移中的“不能在模块外使用import语句”
如何在 nrwl monorep 中调试 NestJS 应用程序