Node版本:v10.13.0
我正在尝试在NodeJS上进行一个简单的测试,涉及到重度CPU计算的请求并发。我知道NodeJS不是处理CPU密集型进程的最佳工具,并且不应该 系统地生成子进程,但是这段代码是为了测试子进程的工作原理。此外,这是使用NestJS编写的TypeScript代码。
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上的不同逻辑核心并发处理(我的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 +----+----+----+-+
前三个请求的行为表现出工作线程池已经饱和,尽管我原本认为会创建三个不同的线程池。后两个请求非常令人困惑,因为它们的行为表现得像是与第三个请求并发执行。
目前我正在寻找以下问题的解释:
- 为什么前三个请求不表现得像是在并发运行
- 为什么最后三个请求表现得像是在并发运行
请注意,如果我添加另一个“快速”方法如下所示:
@Get('fast')
async fast(): Promise<string> {
return 'Fast process ended.';
}
该方法不受并发运行的CPU密集型进程的影响,回复始终即时。