【发布时间】: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