Задать вопрос
@kolomat

Как разбить выполнение функции на разные ядра процессора в node js?

Добрый день, есть функция, по сути кусок парсера, она получает массив ссылок, дальше пробегается по ним, собирает данные и сохраняет.
async function getProductsData(link) {
    let productsUrl = await getProductsUrl(link)
    const cluster = await Cluster.launch({
        concurrency: Cluster.CONCURRENCY_PAGE,
        maxConcurrency: 20,
        monitor: false,
        puppeteerOptions: {
            headless: true,
            defaultViewport: false,
            devtools: false
        },
        timeout: 500000
    });

    cluster.on("taskerror", (err, data) => {
        console.log(`Error crawling ${data}: ${err.message}`);
    });

    let productsData = []

    await cluster.task(async ({page, data: url}) => {
        await page.goto(url)

        let product = await page.evaluate(async () => {
            let description = ''
            let product_attributes = []
            let name = document.querySelector('h1.product-name').innerText.trim()
            let sku = 'SH-' + document.querySelector('span[itemprop=sku]').innerText.trim()
            if (document.querySelector('div[itemprop=description]') !== null) {
                description = document.querySelector('div[itemprop=description]').innerHTML.replace(/\s{2,}/g, ' ').trim()
            }

            document.querySelectorAll('table.attribute > tbody > tr').forEach(el => {
                product_attributes.push('Общая:' + el.querySelector('td:nth-child(1)').innerText + ':' + el.querySelector('td:nth-child(2)').innerText)
            })

            let attributes = product_attributes.join('|').replace(/:\|\s*!/g, '|')

            let obj = {
                name,
                sku,
                description,
                attributes
            }

            return obj
        })
        productsData.push(product)
        fs.writeFileSync('./price/test.json', JSON.stringify(productsData))
    })

    for (url of productsUrl) {
        await cluster.queue(url);
    }

    await cluster.idle();
    await cluster.close();
}

Тоесть в
let productsUrl = await getProductsUrl(link)
Получается обычный одномерный массив ссылок
Суть вопроса в том, например в массиве 1000 ссылок, на сервере 4 ядра, возможно ли как то разбить массив и что бы каждый кластер получил эту функцию и получил по по числу ссылок и паралельно все это обрабатывал?
  • Вопрос задан
  • 162 просмотра
Подписаться 2 Средний 2 комментария
Пригласить эксперта
Ответы на вопрос 2
MvcBox
@MvcBox
Software Engineer [C/C++/JS(for Node.js)/etc]
Комментировать
2ord
@2ord
В общем, создаётся пул из 4*N асинхронных обработчиков очереди productUrls, каждый из которых берет из неё задачи, shift() . Этот пул запускается для асинхронной обработки очереди.

Внутри обработчика productUrlHandler очереди productUrls по окончанию скачивания страницы, кидается в другую очередь parseUrls, из которой обработчик parseUrlHandler выполняет парсинг асинхронно, независимо от скачивания страниц с другой очереди и результат парсинга сохраняет в СУБД или куда надо.

В итоге независимо работают обе очереди, каждая из которых работает в своём темпе и выполняет строго свою работу и одна не мешает другой. При этом, время простоя будет меньше, чем при синхронной обработке и за счёт множества асинхронных функций обратного вызова Node.js они будут распределяться по ядрам процессора.

Поднимать несколько процессов не стоит - достаточно настроить нужный размер пулов обработчиков очередей, 4*M и 4*N. Не нужно выставлять большой размер пула в надежде, что заработает быстрее. По началу, можно взять M=N=1 т.е. пул из 4-ёх обработчиков.

Ни библиотек, ни фреймворков не подскажу. С Puppeteer не работал, помню что некто сказал, что эта штука сильно жрёт память.
Ответ написан
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Похожие вопросы