【问题标题】:http.request()s per second per node.js process每个 node.js 进程每秒 http.request()s
【发布时间】:2013-06-26 01:57:05
【问题描述】:

我正在做一些负载测试并编写节点脚本来做到这一点。我的结果很差,这让我吓坏了,直到我意识到我的测试代码是罪魁祸首。我平均每秒大约 30-50 个请求(进入服务器)使用下面的代码来发出请求。 30-50似乎非常低。这是在 4 核 Mac 上。这是对的还是我做错了什么?

var http = require('http');
var sys = require('util');

http.globalAgent.maxSockets = 100000;

var http = require('http');
http.globalAgent.maxSockets = 100000;

var Request = function (request, params, host, port, completionFn, errorFn)
{
  if(!params)
    params = '';

  if(typeof(errorFn) != 'function')
  {
    errorFn = function (e)
        {
          console.log('request error!? ' + e.message);
          process.exit();
        }
  }

  var paramsStr = '';
  for(var item in params)
    paramsStr += '&' + item + '=' + encodeURI(params[item]);

  var path = '/' + request;
  if(paramsStr != '')
    path += '?' + paramsStr.substr(1);

  var options =
  {
    host:     host,
    port:     port,
    path:     path,
    agent:    false
  };

  http.request(options,

    function (response)
    {
      var responseData = '';

      response.on('data',

        function (chunk)
        {
          responseData += chunk;
        }

      ).on('end',

        function ()
        {
          completionFn(httpRequest.To_JSON(responseData));
        }

      ).on('error', errorFn);
    }

  ).on('error', errorFn).end();
};

新信息:

有趣的是,在 Chrome 中运行它可以让我每秒收到大约 250 个请求,这对于单个节点来说似乎更合理。虽然浏览器确实很快崩溃了。

for(var i = 0; i < 1000000; i++)
{
  $.get('/service', {index:i},function(result){}).error(

    function()
    {
      out.append('fail ');
    }
  );
}

【问题讨论】:

  • 你有什么代码来启动这些请求? Node 并不是一种编写基准测试的特别好的语言。尽管您尝试异步发出请求,但内部事情很可能非常同步地排列。参考我的例子。

标签: javascript node.js http load-testing


【解决方案1】:
/*jshint node:true */
var http = require('http');

var i = 100;

var called = 0;

while (i--) {
    requestGoogle();
}

function requestGoogle() {
    'use strict';
    var requestNum = called++;

    console.log('CALLED: ' + called);
    var options = {
        hostname: 'www.google.com',
        port: 80,
        path: '/',
        method: 'GET'
    };

    var req = http.request(options, function (res) {
        res.setEncoding('utf8');
        res.on('data', function () {
            console.log('RECIEVING DATA ON: ' + requestNum);
        });

        res.on('end', function () {
            console.log('CALLED: ' + --called);
        });
    });

    req.on('error', function (e) {
        console.log('problem with request: ' + e.message);
    });

    req.end();
}

所以,当我运行这个示例时,我注意到两件事。首先是一开始我得到了各种请求的良好数据组合:

接收数据开启:2 接收数据开启:0 接收数据:2 接收数据开启:0 接收数据:3 接收数据:3 接收数据:3 接收数据:3 接收数据:3 接收数据:3 接收数据:3 接收数据:1 接收数据:1 接收数据:1 接收数据:1 接收数据:1 接收数据:1 接收数据:1 接收数据:1 接收数据:4 接收数据:4 接收数据:4

我们看到来自请求 1、4、3、0、2 的块...全部同时进入...太棒了!好吧,过了一段时间,这种行为最终变成了一组非常线性的结果,根本没有发生零混合。最终,我们用排队的请求挤满了事件循环。当我们做这个逻辑服务器端时,它不是立即循环函数调用,将请求排队,实际上是瞬时的。无论有多少请求进入服务器(DDOS 条件下除外),节点进程调用它的事件处理程序并返回主事件循环的周期都很宝贵。

当您执行客户端、while 循环、生成请求时,这些宝贵的几个循环不可用。相反,我们有一个拥挤的事件循环,最终导致您的请求基本上是线性处理的,一旦最初的 10 到 13 个请求接收到数据,它们就会开始按顺序接收数据,并且事情发生的速度比您预期的要慢得多,与节点处理服务器端请求的速度相比,因为我们有一个过度拥挤的事件循环。您的客户端程序本质上已经 DOSed 自己! :)。

您可以尝试通过增加套接字的数量来抑制这种行为,但是,最终您的机器将耗尽文件描述符。此时节点要么崩溃/调用所涉及调用的错误处理程序,要么放慢速度并等待文件描述符变为可用,从而以非常线性、同步的方式处理未来的请求。有很多方法可以增加文件描述符,但最终,要正确进行基准测试,您需要不止一台机器。没有操作系统被设计来处理足够的套接字描述符/网络 I/O 来充分地进行压力测试,即使是单节点服务器。如果是这样的话,DDOS 攻击就容易多了!

另外:看看 JMeter,它对于压力测试的客户端比 node 好得多。

【讨论】:

  • 是的,我没想到单个 CPU 的基准级别吞吐量,只是比我得到的要多。我希望在服务器场上运行测试代码以进行实际测试。
  • 看看JMeter,它对这类事情非常好!如果你真的想使用 Node,请尝试使用 set interval,但设置一个非常小的间隔,而不是立即启动一堆请求。 jmeter.apache.org
  • 虽然你是正确的 50 非常低,但我的机器在运行之前收到了 400 个请求。如果您在 unix 环境中,请尝试使用 ulimit 更改文件描述符。
  • 我快速浏览了 JMeter。看起来很酷。午饭后我会认真看一看……我正在测试一系列需要 if、then、else 逻辑的 AJAX 事务。 JMeter 能做到吗?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-05-03
  • 2018-01-26
  • 1970-01-01
  • 2016-08-19
  • 2020-03-12
  • 2018-03-23
相关资源
最近更新 更多