Не в состоянии выполнить обещания из-за нехватки памяти

У меня есть скрипт, чтобы очистить ~1000 веб-страниц. Я использую Promise.all, чтобы запустить их вместе, и он возвращается, когда все страницы сделаны:

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

Это мило и правильно, за исключением одного - машина выходит из памяти из-за одновременных запросов. Я использую jsdom для утилизации, это быстро занимает несколько ГБ памяти, что понятно, учитывая, что он создает сотни 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... Не является производительным, поскольку он связан, и возвращаемые значения должны быть сохранены для последующей обработки.

Какие-либо предложения? Должен ли я улучшить поток управления, или я должен улучшить использование mem в scrap(), или есть ли способ позволить узлу регулировать распределение памяти?

1 ответ

Решение

Вы пытаетесь запустить 1000 веб-срезов параллельно. Вам нужно будет выбрать какое-то число, значительно меньшее 1000, и одновременно запускать только N, чтобы при этом вы потребляли меньше памяти. Вы все еще можете использовать обещание, чтобы отслеживать, когда они все будут выполнены.

Bluebird - х Promise.map() может сделать это для вас, просто передав значение параллелизма в качестве опции. Или вы можете написать это сами.

У меня есть идея исправить, но мне это не нравится. То есть, измените поток управления, чтобы не использовать Promise.all, но цепочка моих обещаний:

То, что вы хотите, это N операций в полете одновременно. Секвенирование - это особый случай, когда N = 1 что часто будет намного медленнее, чем делать некоторые из них параллельно (возможно, с N = 10).

Это не так хорошо, как Promise.all... Не является производительным, поскольку он связан, и возвращаемые значения должны быть сохранены для последующей обработки.

Если сохраненные значения являются частью вашей проблемы с памятью, вам, возможно, придется хранить их в памяти где-нибудь. Вам придется проанализировать, сколько памяти используют сохраненные результаты.

Какие-либо предложения? Должен ли я улучшить поток управления, или я должен улучшить использование mem в scrap(), или есть ли способ позволить узлу регулировать распределение памяти?

Используйте Bluebird's Promise.map() или напишите что-нибудь подобное самостоятельно. Написание чего-либо, выполняющего до N операций параллельно и сохраняющего все результаты в порядке, не является ракетостроением, но это немного трудоемко, чтобы сделать это правильно. Я представил это раньше в другом ответе, но сейчас не могу найти его. Я буду продолжать искать.

Нашел мой предыдущий связанный ответ здесь: сделайте несколько запросов к API, который может обрабатывать только 20 запросов в минуту

Другие вопросы по тегам