【问题标题】:Lots of parallel http requests in node.jsnode.js 中有很多并行的 http 请求
【发布时间】:2013-06-28 19:39:23
【问题描述】:

我创建了一个 node.js 脚本,它扫描网络以查找可用的 HTTP 页面,因此我想并行运行很多连接,但似乎有些请求等待之前完成。

以下是代码片段:

    var reply = { };
    reply.started = new Date().getTime();
    var req = http.request(options, function(res) {
        reply.status = res.statusCode;
        reply.rawHeaders = res.headers;
        reply.headers = JSON.stringify(res.headers);
        reply.body = '';
        res.setEncoding('utf8');
        res.on('data', function (chunk) {
            reply.body += chunk;
        });
        res.on('end', function () {
            reply.finished = new Date().getTime();
            reply.time = reply.finished - reply.started;
            callback(reply);
        });
    });
    req.on('error', function(e) {
        if(e.message == 'socket hang up') {
            return;
        }
        errCallback(e.message);
    });
    req.end();

此代码每秒仅执行 10-20 个请求,但我需要 500-1k 个请求的性能。每个排队的请求都发送到不同的 HTTP 服务器。

我尝试过这样做,但没有帮助:

    http.globalAgent.maxSockets = 500;

【问题讨论】:

  • 看起来您正在发出 HTTP 请求。是否有可能通过互联网连接快速获得这么多请求?我这里的连接速度非常快,但是我到最近的服务器的 ping 大约是 52 毫秒,我认为这意味着我每秒可以发出大约 20 个 HTTP 请求。
  • 我在一台机器上运行这个脚本,我确信可以处理这么多请求。准确地说:它是 hetzner 6s 服务器。
  • 当然,但是你读过我说的吗?我认为无论您的机器有多强大,您都无法通过 HTTP 使用单个 Internet 连接发出那么多请求。当您发出 HTTP 请求时,您必须等待来自另一端的响应。您当然可以处理比这更多的请求,但那是因为您将处理来自许多浏览器的请求,每个浏览器都有自己的互联网连接。
  • 这里是流行的 http-server 测试工具的输出:#ab -n 10000 -c 1000 srv2.itrack.ru / 每秒请求数:914.94 [#/sec](平均)每个请求的时间:1092.968 [毫秒](平均)
  • 那么你是在 node.js 中排队请求吗?等待回复?你必须这样做,因为处理每个请求需要 1 秒,你需要 node.js 中的 914 个活动线程才能使其工作。

标签: javascript node.js http httprequest


【解决方案1】:

您的代码肯定有其他问题。 Node 每秒可以轻松处理 1k+ 个请求。

我使用以下简单代码进行了测试:

var http = require('http');

var results = [];
var j=0;

// Make 1000 parallel requests:
for (i=0;i<1000;i++) {
    http.request({
        host:'127.0.0.1',
        path:'/'
    },function(res){
        results.push(res.statusCode);
        j++;

        if (j==i) { // last request
            console.log(JSON.stringify(results));
        }
    }).end();
}

为了纯粹测试哪个节点能够而不是我的家庭宽带连接,代码请求来自本地 Nginx 服务器。我还避免使用 console.log,直到所有请求都返回,因为它是作为同步函数实现的(以避免在程序崩溃时丢失调试消息)。

使用time运行代码我得到以下结果:

real    0m1.093s
user    0m0.595s
sys     0m0.154s

1000 个请求需要 1.093 秒,这使得它非常接近每秒 1k 个请求。


如果您尝试发出大量请求(例如 10000 或更多),上面的简单代码将产生操作系统错误,因为节点会很高兴地尝试在 for 循环中打开所有这些套接字(请记住:请求直到for 循环结束,它们只被创建)。您提到您的解决方案也遇到了同样的错误。为避免这种情况,您应该限制发出的并行请求数。

限制并行请求数量的最简单方法是使用async.js 库中的Limit 函数之一:

var http = require('http');
var async = require('async');

var requests = [];

// Build a large list of requests:
for (i=0;i<10000;i++) {
    requests.push(function(callback){
        http.request({
            host:'127.0.0.1',
            path:'/'
        },function(res){
            callback(null,res.statusCode);
        }).end()
    });
}

// Make the requests, 100 at a time
async.parallelLimit(requests, 100,function(err, results){
    console.log(JSON.stringify(results));
});

在我的机器上使用time 运行它,我得到:

real    0m8.882s
user    0m4.036s
sys     0m1.569s

因此,大约 9 秒或大约 1.1k/s 的 10k 请求。

查看async.js提供的功能。

【讨论】:

  • 不错的方法,但“与时俱进”是什么意思?
  • @lesimoes: time 是一个可在包括 Linux 和 MacOS 在内的大多数 unix 操作系统上使用的程序。通常,对于像cdlsgrepawktime 这样的命令,大多数unix 用户会假设每个人都已经知道它们,因此不需要介绍。要使用time 运行程序,只需键入time my_program。在这种情况下,您需要输入 time node my_script.js
  • 不错!非常感谢!
  • 好的,在 DO 上创建一个 1gb 内存的实例,然后告诉我你每秒收到多少请求
【解决方案2】:

我找到了适合我的解决方案,它不是很好,但可以:

childProcess = require('child_process')

我正在使用 curl:

childProcess.exec('curl --max-time 20 --connect-timeout 10 -iSs "' + options.url + '"', function (error, stdout, stderr) { }

这让我可以同时运行 800-1000 个 curl 进程。当然,这个解决方案有它的缺点,比如需要大量打开的文件描述符,但是可以。

我尝试过 node-curl 绑定,但这也很慢。

【讨论】:

    猜你喜欢
    • 2013-06-12
    • 2011-08-06
    • 1970-01-01
    • 2016-03-29
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-07-31
    相关资源
    最近更新 更多