NodeJS http和极大的响应主体

时间:2016-06-04 19:49:15

标签: javascript node.js http

目前,我正在尝试从API(特别是this)请求非常大型JSON对象,这取决于各种因素,可能超过几MB。然而,问题是NodeJS需要永远做任何事情然后只是耗尽内存:我的响应回调的第一行不会执行。

我可以单独请求每个项目,但这是一个巨大的请求。引用新API背后的开发人员:

  

到目前为止,如果您想获得Tranquility的所有市场订单,您必须单独请求每个地区的每种类型。这通常是50多个区域乘以13,000种类型。即使只有13,000种类型和50个地区,也就是获得所有市场信息所需的650,000个请求。如果你想在5分钟缓存窗口中获取所有数据,那么每秒需要大约2,200个请求。

显然,这不是一个好主意。

我尝试将数组items放入redis供以后使用,然后按照next网址重复操作,直至到达最后一页。有没有办法做到这一点?

修改 这是问题代码。访问URL在浏览器中工作正常。

    // ...
    REGIONS.forEach((region) => {
      LOG.info(' * Grabbing data for `' + region.name + '#' + region.id + '`');
      var href = url + region.id + '/orders/all/', next = href;
      var page = 1;
      while (!!next) {
        https.get(next, (res) => {
          LOG.info(' *  * Page ' + page++ + ' responded with ' + res.statusCode);
      // ...

执行第一个LOG.info行,而第二行不执行。

3 个答案:

答案 0 :(得分:3)

您似乎正在进行导致问题的while(!!next)循环。如果你展示了更多的服务器代码,我们可以更准确地提出建议,甚至建议一种更好的编码方式。

Javascript运行你的代码单线程。这意味着在执行任何其他事件之前,一个执行线程将运行完毕。

所以,如果你这样做:

while(!!next) {
    https.get(..., (res) => {
        // hoping this will run
    });
}

然后,您的http.get()回调将永远不会被调用。你的while循环只是永远运行。只要它正在运行,https.get()的回调就永远不会被调用。该请求可能很久就已完成,并且有一个事件位于内部JS事件队列中以调用回调,但在您的while()循环完成之前,该事件无法被调用。所以你有一个僵局。 while()循环正在等待运行其他东西来改变它的状态,但在while()循环完成之前没有其他任何东西可以运行。

还有其他几种方法可以进行串行异步迭代。通常,您无法使用.forEach()while()

以下是异步循环的几种方案:

Node.js: How do you handle callbacks in a loop?

While loop with jQuery async AJAX calls

How to synchronize a sequence of promises?

How to use after and each in conjunction to create a synchronous loop in underscore js

或者,您提到的异步库也具有执行异步循环的功能。

答案 1 :(得分:1)

首先,几MB的json有效载荷并不是很大。因此路由处理程序代码可能需要仔细审查。

但是,要实际处理大量JSON,您可以将您的请求作为流使用。 JSONStream(以及许多其他类似的库)允许您以内存有效的方式执行此操作。您可以使用JSONPath(用于JSON的XPath模拟)指定需要处理的路径,然后订阅流以匹配数据集。

以下来自JSONStream的README的示例简洁地说明了这一点:

var request = require('request')
  , JSONStream = require('JSONStream')
  , es = require('event-stream')

request({url: 'http://isaacs.couchone.com/registry/_all_docs'})
  .pipe(JSONStream.parse('rows.*'))
  .pipe(es.mapSync(function (data) {
    console.error(data)
    return data
  }))

答案 2 :(得分:0)

使用请求模块的流功能来处理大量传入数据。当数据通过流传递时,将其解析为可以使用的数据块,通过管道推送数据,然后拉入下一个数据块。

您可以创建一个转换流来处理已解析的数据块和用于存储数据块的写入流。

例如:

var stream = request ({ url: your_url }).pipe(parseStream)
    .pipe(transformStream)
   .pipe (writeStream);

stream.on('finish', () => {
    setImmediate (() => process.exit(0));
});

尝试有关创建流https://bl.ocks.org/joyrexus/10026630

的信息