在NestJS HTTP服务器中使用子进程时,CPU绑定的进程会阻止工作池

Bob*_*Bob 13 child-process httpserver node.js nestjs

节点版本: 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;
  }
}
Run Code Online (Sandbox Code Playgroud)

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' });
});
Run Code Online (Sandbox Code Playgroud)

如果在没有产生新的子进程的情况下执行这样的长进程,则会导致结果的时间轴,有5个并发请求(从#1到#5).每个进程阻塞循环事件,每个请求必须等待前一个进程完成才能得到回答.

Time 0    10   20   30   40   50
#1   +----+
#2   +----+----+
#3   +----+----+----+
#4   +----+----+----+----+
#5   +----+----+----+----+----+
Run Code Online (Sandbox Code Playgroud)

在产生新的子进程时,我期望每个进程将由我的CPU上的不同逻辑核同时处理(我的有8个逻辑核),导致这个预测的时间线:

Time 0    10   20   30   40   50
#1   +----+
#2   +----+
#3   +----+
#4   +----+
#5   +----+
Run Code Online (Sandbox Code Playgroud)

虽然,在每次测试中观察到这个奇怪的结果:

Time 0    10   20   30   40   50
#1   +----+
#2   +----+----+
#3   +----+----+----+
#4   +----+----+----++
#5   +----+----+----+-+
Run Code Online (Sandbox Code Playgroud)

前3个请求就像工作池被饿了一样,尽管我假设已经创建了3个不同的池.最后两个请求非常令人困惑,因为它们的行为与请求#3同时工作.

我正在寻找以下解释:

  • 为什么前3个请求不像同时运行那样
  • 为什么最后3个请求的行为就像同时运行一样

请注意,如果我添加另一个"快速"方法,如下所示:

  @Get('fast')
  async fast(): Promise<string> {
    return 'Fast process ended.';
  }
Run Code Online (Sandbox Code Playgroud)

此方法不受并发运行的CPU密集型进程的影响,并且总是立即回复.

Aab*_*bid 2

我在我的机器上执行了测试用例,它工作正常,你可以在你的机器上检查一下吗?

节点版本:v8.11.2 操作系统:macOs High Sierra 10.13.4,8 核

子进程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);  
    });
}
Run Code Online (Sandbox Code Playgroud)

cpu 密集型-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' });
Run Code Online (Sandbox Code Playgroud)

输出

在此输入图像描述

您可以检查输出,差异仅适用10 sec于所有过程,您可以在您的计算机上执行此测试用例并让我知道,可能会有所帮助。