【问题标题】:Node.js Http.request slows down under load testing. Am I doing something wrong?Node.js Http.request 在负载测试下变慢。难道我做错了什么?
【发布时间】:2011-06-01 23:55:34
【问题描述】:

这是我的示例代码:

var http = require('http');

var options1 = {
          host: 'www.google.com',
          port: 80,
          path: '/',
          method: 'GET'
        };

http.createServer(function (req, res) {

        var start = new Date();
        var myCounter = req.query['myCounter'] || 0;

        var isSent = false;
        http.request(options1, function(response) {
            response.setEncoding('utf8');
            response.on('data', function (chunk) {
                var end = new Date();
                console.log(myCounter + ' BODY: ' + chunk  + " time: " + (end-start) + " Request start time: " + start.getTime());

                if (! isSent) {
                    isSent = true;
                    res.writeHead(200, {'Content-Type': 'application/xml'});
                    res.end(chunk);
                }
            });
        }).end();


}).listen(3013);

console.log('Server running at port 3013');

我发现,如果我连接到其他服务器(谷歌或任何其他服务器),响应会越来越慢到几秒钟。如果我连接到同一网络中的另一个 node.js 服务器,则不会发生这种情况。

我使用 JMeter 进行测试。每秒 50 个并发,1000 个循环。

我不知道问题是什么......

===========================

进一步调查:

我在 Rackspace 和 EC2 上运行相同的脚本进行测试。该脚本将使用 http.request 连接到:Google、Facebook 以及我的另一个脚本,该脚本仅输出由另一个 EC2 实例托管的数据(如 hello world)。

我刚才的测试工具是我桌面上的jMeter。

pre-node.js 测试: jMeter -> Google 结果:快速且一致。 jMeter -> Facebook 结果:快速且一致。 jMeter -> 我的简单输出脚本结果:快速且一致。

然后我创建一个 50 Concurrent Threads /sec ,有 100 个循环,测试我的 Rackspace nodejs,然后是 EC2 node.js,它具有相同的性能问题之王 jMeter -> node.js -> Google 结果:在 200 个请求中从 50 毫秒变为 2000 毫秒。
jMeter -> node.js -> Facebook 结果:200 次请求后从 200 毫秒变为 3000 毫秒。
jMeter -> node.js -> 我的简单输出脚本结果:在 200 次请求后从 100 毫秒变为 1000 毫秒。
前 10-20 个请求很快,然后开始变慢。

然后,当我更改为 10 个并发线程时,情况开始发生变化.. 响应非常一致,没有变慢。

与 Node.js (http.request) 可以处理的并发线程数有关。

------------ 更多 --------------

我今天做了更多测试,结果如下: 我使用了 http.Agent 并增加了最大套接字。然而,有趣的是,在一个测试服务器 (EC2) 上,它改进了很多并且不再减慢。然而,其他服务器(机架空间)只改进了一点。它仍然显示缓慢。我什至在请求标头中设置了“连接:关闭”,它只提高了 100 毫秒。

如果http.request使用了连接池,如何增加?

在两个服务器中,如果我执行“ulimit -a”,打开的文件数为 1024。

------------- ** 越来越多 ** --------

似乎即使我将 maxSockets 设置为更高的数字,它也只能在某些限制下工作。似乎存在内部或操作系统相关的套接字限制。然而要顶一下?

------------- ** 经过大量测试 ** ---------------

看了很多帖子,我发现:



引用自:https://github.com/joyent/node/issues/877


1) 如果我用 connection = 'keep-alive' 设置标头,性能很好并且可以达到 maxSocket = 1024(这是我的 linux 设置)。

var options1 = {
                  host: 'www.google.com',
                  port: 80,
                  path: '/',
                  method: 'GET',
    **headers: {
            'Connection':'keep-alive'
    }**
                };

如果我将它设置为“连接”:“关闭”,响应时间会慢 100 倍。

这里发生了有趣的事情:

1) 在 EC2 中,当我第一次使用 Connection:keep-alive 进行测试时,大约需要 20-30 毫秒。然后,如果我更改为 Connection:Close 或设置 Agent:false,响应时间将减慢到 300 毫秒。如果不重新启动服务器,如果我再次更改为 Connection:keep-alive,响应时间将进一步减慢至 4000 毫秒。要么我必须重新启动服务器,要么等待一段时间才能恢复我 20-30 毫秒的光速响应。

2) 如果我使用 agent:false 运行它,一开始响应时间会减慢到 300 毫秒。但随后,它会再次变得更快并恢复“正常”。

我的猜测是即使您设置了 agent:false,连接池仍然有效。但是,如果您保持连接:保持活动状态,那么它肯定会很快。只是不要切换它。




2011 年 7 月 25 日更新

我尝试了最新的 node.js V0.4.9 以及来自https://github.com/mikeal/node/tree/http2 的 http.js 和 https.js 修复

性能更好更稳定。

【问题讨论】:

  • 你应该监听response.on('end')事件,而不是这样做isSent魔术。
  • 我遇到了同样的问题,并且通过保持活动更改看到了相当大的性能提升,但是在负载测试一段时间后我遇到了错误。以下是错误:{ [Error: connect EADDRNOTAVAIL] code: 'EADDRNOTAVAIL', errno: 'EADDRNOTAVAIL', syscall: 'connect' }
  • 此评论中的链接现在给出 404 错误。

标签: http node.js request load-testing


【解决方案1】:

我解决了这个问题

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

agent = new http.Agent()
agent.maxSockets = 1000000  # 1 million
http.request({agent:agent})

【讨论】:

    【解决方案2】:

    我也在为同样的问题而苦苦挣扎。我发现我的瓶颈是 DNS 的东西,虽然我不太清楚在哪里/为什么。如果我向http://myserver.com/asd 之类的请求发出请求,我几乎无法以 50-100 rq/s 的速度运行,如果超过 100 rq/s 并且更多的事情会变成灾难,响应时间会变得很长,并且一些请求永远不会完成并无限期地等待我需要杀死 -9 我的服务器。如果我向服务器的 IP 地址发出请求,则一切都稳定在 500 rq/s,尽管并不完全平滑,而且图形(我有实时图形)是峰值。并且要注意在 Linux 中打开文件的数量仍然是有限的,我设法击中了一次。另一个观察是单节点进程不能顺利地做出 500 rq/s。但是我可以启动 4 个节点进程,每个进程 200 rq/s,我得到非常平滑的图形和一致的 CPU/网络负载和非常短的响应时间。这是节点 0.10.22。

    【讨论】:

    • 原来这是一个带有 DNS 的 nodejs 错误。我不能肯定地说,但从我的代码检查来看,似乎有一种情况可以在事件发生后分配“dns resolved”回调,但在发现不稳定版本中这个方向发生了变化后,我停止了深入挖掘节点,问题在那里不明显。似乎已在节点 0.11.9 中修复,并且我的无限请求问题并未出现在那里。
    【解决方案3】:

    这不一定能解决您的问题,但它会稍微清理您的代码并以您应该的方式利用各种事件:

    var http = require('http');
    
    var options1 = {
          host: 'www.google.com',
          port: 80,
          path: '/',
          method: 'GET'
    };
    
    http.createServer(function (req, res) {
        var start = new Date();
        var myCounter = req.query['myCounter'] || 0;
    
        http.request(options1, function(response) {
            res.on('drain', function () { // when output stream's buffer drained 
                response.resume(); // continue to receive from input stream
            });
            response.setEncoding('utf8');
            res.writeHead(response.statusCode, {'Content-Type': 'application/xml'});
            response.on('data', function (chunk) {
                if (!res.write(chunk)) { // if write failed, the stream is choking
                    response.pause(); // tell the incoming stream to wait until output stream drained
                }
            }).on('end', function () {
                var end = new Date();
                console.log(myCounter + ' time: ' + (end-start) + " Request start time: " + start.getTime());
                res.end();
            });
        }).end();
    }).listen(3013);
    
    console.log('Server running at port 3013');
    

    我删除了身体的输出。由于我们是从一个套接字流式传输到另一个套接字,因此如果不缓冲它,我们无法确保在任何时候都能看到整个主体。

    编辑:我相信节点正在使用连接池来处理 http.request。如果您有 50 个并发连接(因此有 50 个并发 http.request 尝试),您可能会遇到连接池限制。我目前没有时间为您查找,但您应该查看有关 http 的节点文档,尤其是 http 代理。

    编辑 2:a thread 关于 node.js 邮件列表中的一个非常相似的问题。你应该看看它,尤其是 Mikael 的帖子应该很有趣。他建议通过将选项agent: false 传递给http.request 调用来完全关闭请求的连接池。我没有任何进一步的线索,所以如果这没有帮助,也许你应该尝试在 node.js 邮件列表上获得帮助。

    【讨论】:

    • 还是一样。我在 Rackspace 服务器上运行,现在,我将在 EC2 服务器上尝试它,看看它是否有任何不同。该死的... :(
    • 嗨。我今天做了一个测试。请在上面的原始帖子中查看我的更新。 :)
    • 请看我的第二次编辑。我不知道为什么 EC2 和 Rackspace 之间的行为存在差异。也许是由于带宽限制?
    • 我只是运行一个测试。设置 agent:false 没有影响。连接池是否也受硬件或操作系统设置的限制/限制?
    • @Daniel 您可能正在使用代理的最大连接数,它默认为五个,我没有看到上面的代码改变了当前服务器github.com/joyent/node/blob/master/lib/http.js#L1139跨度>
    【解决方案4】:

    Github issue 877 可能相关:

    https://github.com/joyent/node/issues/877

    虽然我不清楚这是否是你要打的。当我点击它时,“代理:假”解决方法对我有用,在请求中设置“连接:保持活动”标头也是如此。

    【讨论】:

    • 我发现,如果将agent:false设置为connection:keep-alive,速度是一致的,但有点慢。如果代理设置了 connection:keep-alive,有时,前几个请求会比较慢,但随后会非常快。 (比代理:假快)。如果没有设置连接,或者连接:关闭,那么这将是一场灾难。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2019-08-16
    • 1970-01-01
    • 1970-01-01
    • 2023-03-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多