【问题标题】:unable to complete promises due to out of memory由于内存不足无法完成承诺
【发布时间】:2016-11-29 03:39:43
【问题描述】:

我有一个脚本可以抓取大约 1000 个网页。我正在使用 Promise.all 将它们一起触发,并在所有页面完成后返回:

Promise.all(urls.map(url => scrap(url)))
    .then(results => console.log('all done!', results));

这是甜蜜和正确的,除了一件事 - 由于并发请求,机器内存不足。我使用 jsdom 进行报废,它很快占用了几 GB 的内存,考虑到它实例化了数百个 window,这是可以理解的。

我有一个想法要解决,但我不喜欢它。也就是说,将控制流更改为不使用 Promise.all,而是链接我的承诺:

let results = {};
urls.reduce((prev, cur) =>
    prev
        .then(() => scrap(cur))
        .then(result => results[cur] = result)
        // ^ not so nice. 
, Promise.resolve())
    .then(() => console.log('all done!', results));

这不如 Promise.all... 性能不佳,因为它是链式的,并且必须存储返回值以供以后处理。

有什么建议吗?我应该改进控制流还是应该改进 scrap() 中的 mem 使用,或者有没有办法让节点限制 mem 分配?

【问题讨论】:

  • 我不明白你所说的“因为它被链接了所以性能不佳
  • 顺便说一句,必须是.then(() => scrap(cur))
  • @Bergi 也许我在这里错了。我认为缓慢的部分是向 url 发出请求。在链式版本中,您只能在我们对上一个 url 完成所有报废工作后触发下一个请求。在 Promise.all 版本中,它们都可以启动(发送http请求当然是异步的),并在它们返回时进行处理
  • @Bergi 固定.then(() => scrap(cur))
  • @CharlesW:理想情况下,您希望将下载与抓取分开。下载是低 cpu / mem 并且可以并行完成数十个(如果不是 1000 个)url。抓取,即:将原始 html 处理成您想要的形式在 cpu 负载上很高(并且内存取决于您所使用的抓取解决方案)。如果可能的话,把它们分开会让你走得很远。如果不可能,我会简单地将下载/抓取成批,比如 10 个而不是 1000 个。

标签: node.js memory-management promise es6-promise jsdom


【解决方案1】:

您正在尝试并行运行 1000 次网络抓取。您将需要选择一个明显小于 1000 的数字,并且一次只运行 N 个,这样您在执行此操作时会消耗更少的内存。您仍然可以使用 Promise 来跟踪它们何时完成。

Bluebird's Promise.map() 只需传递一个并发值作为选项即可为您做到这一点。或者,您可以自己编写。

我有一个要解决的想法,但我不喜欢它。也就是变更控制 流程不使用 Promise.all,而是链接我的承诺:

您想要的是同时进行 N 个操作。排序是一种特殊情况,其中N = 1 通常比并行执行其中一些要慢得多(可能使用N = 10)。

这不如 Promise.all... 性能不佳,因为它是链式的, 并且必须存储返回值以供以后处理。

如果存储的值是您的内存问题的一部分,您可能不得不以任何方式将它们存储在内存之外的某个地方。您必须分析存储的结果使用了多少内存。

有什么建议吗?我应该改进控制流还是应该改进 scrap() 中的 mem 使用情况,或者有没有办法让节点限制 mem 分配?

使用Bluebird's Promise.map() 或自己编写类似的内容。编写可以并行运行多达 N 次操作并保持所有结果有序的东西并不是火箭科学,但要让它正确,需要一些工作。我之前在另一个答案中提出过它,但现在似乎找不到它。我会继续寻找。

在这里找到我之前的相关答案:Make several requests to an API that can only handle 20 request a minute

【讨论】:

  • 添加了解决此问题的参考代码链接。
  • 解决了我的问题! bluebird的附加功能非常好用
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2019-04-06
  • 1970-01-01
  • 2017-06-17
  • 2022-01-12
  • 1970-01-01
  • 1970-01-01
  • 2021-07-06
相关资源
最近更新 更多