Утечка памяти при вызове слишком большого количества обещаний в Nodejs / Request / MongoDB - PullRequest
0 голосов
/ 02 марта 2020

Когда я пытался вызвать до 200 000 POST-запросов в NodeJS, он отображал некоторые ошибки, такие как утечка кучи памяти.

В каждом POST-запросе я хочу вставить разрешенные данные в localhost mon go DB.

Можно сделать 2000 запросов одновременно, но с 200 000 запросов действительно сложно справиться. Я застрял в этой проблеме и не знаю точно, как ее решить.

Мне действительно нужна ваша помощь или какие-либо предложения.

Заранее благодарю за помощь.

    const mongoose = require('mongoose');
    const request = require('request');

    // DB connection
    mongoose
        .connect("mongodb://localhost:27017/test?retryWrites=true&w=majority", { useNewUrlParser: true, useUnifiedTopology: true })
        .then(() => console.log('Connected!'))
        .catch(err => console.error('Could not connect...', err));

    // Initialize Mongoose 's model
    const Sample = mongoose.model(
        'Sample',
        new mongoose.Schema({}, { strict: false, versionKey: false }),
        'sample_collection'
    );

    // Insert data into Sample
    var insertDataIntoSample = function (means) {
        Sample.collection.insert(means, { ordered: false });
    }

    // HTTP POST request to get data
    const getDataFromInternet = function (param) {
        return new Promise((resolve, reject) => {
            request.post(
                'https://url-to-post-data.com/',
                { json: { 'query': param } },
                function (error, response, body) {
                    if (!error && response.statusCode == 200 && body) {
                        insertDataIntoSample(body.data);
                        resolve(param);
                    }
                }
            );
        });
    };

    // Call up to 200,000 requests
    var myParams = [...] // 200,000 elements
    for (var i = 0; i < myParams.length; i++) {
        getDataFromInternet(myParams[i]).then(function (data) {
            console.log(data)
        })
    }

1 Ответ

0 голосов
/ 02 марта 2020

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

С небольшим тестированием вы бы приблизительно выяснили, сколько одновременных запросов все еще эффективно, и это полностью зависит от вашей базы данных и ее конфигурации. Большой железный сервер баз данных может иметь доступ к большому количеству процессоров / потоков и, возможно, даже к некоторым эффективным разделам диска и иметь возможность выполнять несколько запросов одновременно. Меньшая конфигурация может ничего не получить после нескольких запросов в полете за один раз.

Здесь есть несколько десятков опций в stackoverflow и в других местах для выполнения асинхронного вызова функции при обработке массива и выполнения его так, чтобы только N запросов находятся в полете одновременно. Это, вероятно, общая концепция, которую вы хотите здесь. В библиотеках, таких как Bluebird и Asyn c -Promises, есть встроенные функции для управления одновременным доступом.

Один из моих простых фаворитов (просто функцию, которую вы можете скопировать) называется mapConcurrent(). Вы передаете ему массив, максимальное количество запросов, которые вы хотите выполнить за один раз, и функцию возврата обещания, которую она будет вызывать для каждого элемента в массиве.

Вы запускаете эксперименты с вашей конфигурацией, чтобы увидеть, что оптимальное значение для maxConcurrent - это (подсказка, вероятно, это довольно небольшое число, например, меньше 10).

// takes an array of items and a function that returns a promise
function mapConcurrent(items, maxConcurrent, fn) {
    let index = 0;
    let inFlightCntr = 0;
    let doneCntr = 0;
    let results = new Array(items.length);
    let stop = false;

    return new Promise(function(resolve, reject) {

        function runNext() {
            let i = index;
            ++inFlightCntr;
            fn(items[index], index++).then(function(val) {
                ++doneCntr;
                --inFlightCntr;
                results[i] = val;
                run();
            }, function(err) {
                // set flag so we don't launch any more requests
                stop = true;
                reject(err);
            });
        }

        function run() {
            // launch as many as we're allowed to
            while (!stop && inflightCntr < maxConcurrent && index < items.length) {
                runNext();
            }
            // if all are done, then resolve parent promise with results
            if (doneCntr === items.length) {
                resolve(results);
            }
        }

        run();
    });
}

В этом ответе упоминаются некоторые другие параметры Пакетная асинхронная операция .

...