Node.js集群没有'不会显著提高性能

Node.js cluster doesn't significantly improve performance

本文关键字:高性能 js Node      更新时间:2023-09-26

如果有人想尝试:https://github.com/codependent/cluster-performance

我正在用一个简单的应用程序测试Node.js(v0.11.13-Windows7)每秒的请求限制。我已经用Express4实现了一个服务,它模拟I/O操作,例如带有setTimeout回调的DB查询。

首先,我只使用一个节点进程来测试它。在第二个测试中,我启动了与机器的CPU一样多的工作程序。

我正在使用负载测试来测试具有以下参数的服务:

loadtest -n 50000 -c 220 -k http://localhost:5000/operations/timeout/20

也就是说,总共有5万个请求,220个并发客户端。

我的服务根据最后一个url参数(20毫秒)设置超时(处理时间的持续时间):

router.route('/timeout/:time')
.get(function(req, res) {
    setTimeout(function(){
        appLog.debug("Timeout completed %d", process.pid);
        res.json(200,{result:process.pid});
    },req.param('time'));
});    
  1. 只有一个节点进程

以下是结果:

INFO Max requests:        50000
INFO Concurrency level:   200
INFO Agent:               keepalive
INFO
INFO Completed requests:  50000
INFO Total errors:        0
INFO Total time:          19.326443741 s
INFO Requests per second: 2587
INFO Total time:          19.326443741 s
INFO
INFO Percentage of the requests served within a certain time
INFO   50%      75 ms
INFO   90%      92 ms
INFO   95%      100 ms
INFO   99%      117 ms
INFO  100%      238 ms (longest request)

每秒2580个请求,不错。

  1. n个工人(n=numCPU)

在这种情况下,我使用循环调度策略在工人之间平均分配负载。由于现在有8个内核处理请求,我原本预计每秒请求的结果会有显著的提高(快8倍?),但只增加到2905个rps!!!(318转/秒以上)你怎么解释?我做错什么了吗?

结果:

Max requests:        50000
Concurrency level:   220
Agent:               keepalive
Completed requests:  50000
Total errors:        0
Total time:          17.209989764000003 s
Requests per second: 2905
Total time:          17.209989764000003 s
Percentage of the requests served within a certain time
  50%      69 ms
  90%      103 ms
  95%      112 ms
  99%      143 ms
 100%      284 ms (longest request)

我的集群初始化代码:

#!/usr/bin/env node
var nconf = require('../lib/config');
var app = require('express')();
var debug = require('debug')('mma-nodevents');
var http = require("http")
var appConfigurer = require('../app');
var cluster = require('cluster');
var numCPUs = require('os').cpus().length;
if('v0.11.13'.localeCompare(process.version)>=0){
    cluster.schedulingPolicy = cluster.SCHED_RR;
}
if (cluster.isMaster) {
    // Fork workers.
    for (var i = 0; i < numCPUs; i++) {
        cluster.fork();
    }
    cluster.on('exit', function(worker, code, signal) {
        console.log('worker ' + worker.process.pid + ' died');
        cluster.fork();
    });
}else{
    console.log("starting worker [%d]",process.pid);
    appConfigurer(app);
    var server = http.createServer(app);
    server.listen(nconf.get('port'), function(){
        debug('Express server listening on port ' + nconf.get('port'));
    });
}
module.exports = app;

更新:

我终于接受了slebetman的回答,因为他对本例中集群性能在多达8个进程的情况下没有显著提高的原因是正确的。然而,我想指出一个有趣的事实:使用当前io.js版本(2.4.0),即使对于这种高I/O操作(setTimeout),它也确实有所改进:

loadtest -n 50000 -c 220 -k http://localhost:5000/operations/timeout/20

单线程

Max requests:        50000
Concurrency level:   220
Agent:               keepalive
Completed requests:  50000
Total errors:        0
Total time:          13.391324847 s
Requests per second: 3734
Total time:          13.391324847 s
Percentage of the requests served within a certain time
  50%      57 ms
  90%      67 ms
  95%      74 ms
  99%      118 ms
 100%      230 ms (longest request)

8核心集群:

Max requests:        50000
Concurrency level:   220
Agent:               keepalive
Completed requests:  50000
Total errors:        0
Total time:          8.253544166 s
Requests per second: 6058
Total time:          8.253544166 s
Percentage of the requests served within a certain time
  50%      35 ms
  90%      47 ms
  95%      52 ms
  99%      68 ms
 100%      178 ms (longest request)

因此,很明显,在当前的io.js/node.js版本中,尽管你没有获得8倍的rps增长,但吞吐量几乎快了1.7倍。

另一方面,正如预期的那样,使用for循环迭代请求中指示的毫秒数(从而阻塞线程),rps与线程数成比例增加。

I/O操作正是Node.js设计和优化的应用程序类型。I/O操作(和setTimeout)基本上是在硬件(网络、磁盘、PCI桥、DMA控制器等)允许的情况下并行运行的。

一旦意识到这一点,就很容易理解为什么在单个进程中运行许多并行I/O操作所花费的时间与在许多进程/线程中运行许多平行I/O操作所花的时间大致相同。事实上,在一个进程中运行许多并行I/O操作的直接模拟与在许多并行进程中运行单个阻塞I/O操作完全相同。

集群允许您使用多个CPU/内核(如果您有)。但是您的进程不使用CPU周期。因此,集群给您带来的优势非常小(如果有的话)。

您是否尝试过将负载测试程序本身拆分为两个或多个进程?完全有可能您已经达到了负载测试应用程序的极限。

一个简单的计算:

1000/20*220 = 11000   // theoretically max request per second

您在localhost上测试,这意味着使用的网络时间很小,所以我想日志输出是块

appLog.debug("Timeout completed %d", process.pid);

请对此进行评论,然后重试。

不要使用cluster.SCHED_RR,只使用cluster.SCHED+