在负载测试下,Node.js Http.request变慢了。 难道我做错了什么?

Node.js Http.request slows down under load testing. Am I doing something wrong?

这是我的示例代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
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个并发线程/秒,并进行了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)上,它有很多改进,并且不再降低速度。但是,其他服务器(机架空间)仅改善了一点。它仍然显示速度变慢。我什至在请求标头中设置了" Connection:close",它仅改善了100ms。

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

在两台服务器上,如果我执行" ulimit -a",则打开的文件数为1024。

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

似乎即使我将maxSockets设置为更高的数字,它也只能在某个限制下工作。似乎存在内部或操作系统相关的套接字限制。但是要撞到它吗?

------------- **经过广泛测试后** ---------------

阅读大量文章后,我发现:

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

1)如果我使用connection ='keep-alive'设置标头,则性能良好,最高可以达到maxSocket = 1024(这是我的Linux设置)。

1
2
3
4
5
6
7
8
9
var options1 = {
                  host: 'www.google.com',
                  port: 80,
                  path: '/',
                  method: 'GET',
    **headers: {
            'Connection':'keep-alive'
    }**
                };

如果将其设置为" Connection":" close",响应时间将慢100倍。

有趣的事情发生在这里:

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

2)如果我使用agent:false运行它,起初,响应时间将减慢到300ms。但是,它将再次变得更快,并恢复到"正常"状态。

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

2011年7月25日更新

我尝试通过https://github.com/mikeal/node/tree/http2上的http.js和https.js修复程序尝试最新的node.js V0.4.9

性能更好,更稳定。


我解决了

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

要么

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

我在同一个问题上挣扎。尽管我不清楚在哪里/为什么,但我发现瓶颈是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。


Github问题877可能与以下内容有关:

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

尽管我不清楚这是否是您要解决的问题。当我遇到该问题时," agent:false"变通方法为我工作,对请求设置" connection:keep-alive"标头也是如此。


这不一定可以解决您的问题,但是可以稍微清理一下代码,并以应有的方式利用各种事件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
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:关于node.js邮件列表中非常相似的问题,有一个主题。您应该看一下它,尤其是Mikael的帖子应该引起人们的兴趣。他建议通过将选项agent: false传递给http.request调用来完全关闭请求的连接池。我没有任何其他线索,因此,如果这无济于事,也许您应该尝试在node.js邮件列表上寻求帮助。