Подтвердить что ты не робот

Node.js Запрос GET ETIMEDOUT & ESOCKETTIMEDOUT

Я использую Node.js - модуль async и request для сканирования более чем 100 миллионов веб-сайтов, и я продолжаю сталкиваться с ошибками ESOCKETTIMEDOUT и ETIMEDOUT через несколько минут.

Он снова работает после перезапуска script. Это, по-видимому, не проблема ограничения соединения, потому что я все еще могу без проблем выполнить разрешения4, resolveNs, resolveMx, а также curl.

Вы видите какие-либо проблемы с кодом? или какие-либо советы? Я хотел бы увеличить async.queue() concurrency до 1000. Спасибо.

var request = require('request'),
    async = require('async'),
    mysql = require('mysql'),
    dns = require('dns'),
    url = require('url'),
    cheerio = require('cheerio'),
    iconv = require('iconv-lite'),
    charset = require('charset'),
    config = require('./spy.config'),
    pool = mysql.createPool(config.db);

iconv.skipDecodeWarning = true;

var queue = async.queue(function (task, cb) {
    dns.resolve4('www.' + task.domain, function (err, addresses) {
        if (err) {
            //
            // Do something
            //
            setImmediate(function () {
                cb()
            });
        } else {
            request({
                url: 'http://www.' + task.domain,
                method: 'GET',
                encoding:       'binary',
                followRedirect: true,
                pool:           false,
                pool:           { maxSockets: 1000 },
                timeout:        15000 // 15 sec
            }, function (error, response, body) {

                //console.info(task);

                if (!error) {
                  // If ok, do something

                } else {
                    // If not ok, do these

                    console.log(error);

                    // It keeps erroring here after few minutes, resolve4, resolveNs, resolveMx still work here.

                    // { [Error: ETIMEDOUT] code: 'ETIMEDOUT' }
                    // { [Error: ESOCKETTIMEDOUT] code: 'ESOCKETTIMEDOUT' }

                    var ns = [],
                        ip = [],
                        mx = [];
                    async.parallel([
                        function (callback) {
                            // Resolves the domain name server records
                            dns.resolveNs(task.domain, function (err, addresses) {
                                if (!err) {
                                    ns = addresses;
                                }
                                callback();
                            });
                        }, function (callback) {
                            // Resolves the domain IPV4 addresses
                            dns.resolve4(task.domain, function (err, addresses) {
                                if (!err) {
                                    ip = addresses;
                                }
                                callback();
                            });
                        }, function (callback) {
                            // Resolves the domain MX records
                            dns.resolveMx(task.domain, function (err, addresses) {
                                if (!err) {
                                    addresses.forEach(function (a) {
                                        mx.push(a.exchange);
                                    });
                                }
                                callback();
                            });
                        }
                    ], function (err) {
                        if (err) return next(err);

                        // do something
                    });

                }
                setImmediate(function () {
                    cb()
                });
            });
        }
    });
}, 200);

// When the queue is emptied we want to check if we're done
queue.drain = function () {
    setImmediate(function () {
        checkDone()
    });
};
function consoleLog(msg) {
    //console.info(msg);
}
function checkDone() {
    if (queue.length() == 0) {
        setImmediate(function () {
            crawlQueue()
        });
    } else {
        console.log("checkDone() not zero");
    }
}

function query(sql) {
    pool.getConnection(function (err, connection) {
        if (!err) {
            //console.log(sql);
            connection.query(sql, function (err, results) {
                connection.release();
            });
        }
    });
}

function crawlQueue() {
    pool.getConnection(function (err, connection) {
        if (!err) {
            var sql = "SELECT * FROM domain last_update < (UNIX_TIMESTAMP() - 2592000) LIMIT 500";
            connection.query(sql, function (err, results) {
                if (!err) {
                    if (results.length) {
                        for (var i = 0, len = results.length; i < len; ++i) {
                            queue.push({"id": results[i]['id'], "domain": results[i]['domain'] });
                        }
                    } else {
                        process.exit();
                    }
                    connection.release();
                } else {
                    connection.release();
                    setImmediate(function () {
                        crawlQueue()
                    });
                }
            });
        } else {
            setImmediate(function () {
                crawlQueue()
            });
        }
    });
}
setImmediate(function () {
    crawlQueue()
});

И системные ограничения довольно высоки.

    Limit                     Soft Limit           Hard Limit           Units
    Max cpu time              unlimited            unlimited            seconds
    Max file size             unlimited            unlimited            bytes
    Max data size             unlimited            unlimited            bytes
    Max stack size            8388608              unlimited            bytes
    Max core file size        0                    unlimited            bytes
    Max resident set          unlimited            unlimited            bytes
    Max processes             257645               257645               processes
    Max open files            500000               500000               files
    Max locked memory         65536                65536                bytes
    Max address space         unlimited            unlimited            bytes
    Max file locks            unlimited            unlimited            locks
    Max pending signals       257645               257645               signals
    Max msgqueue size         819200               819200               bytes
    Max nice priority         0                    0
    Max realtime priority     0                    0
    Max realtime timeout      unlimited            unlimited            us

Sysctl

net.ipv4.ip_local_port_range = 10000    61000
4b9b3361

Ответ 1

По умолчанию в Node есть 4 рабочих для разрешения DNS-запросов. Если ваш DNS-запрос занимает много времени, запросы будут блокироваться на фазе DNS, и симптом будет точно ESOCKETTIMEDOUT или ETIMEDOUT.

Попробуйте увеличить размер пула ультрафиолетовых потоков:

export UV_THREADPOOL_SIZE=128
node ...

или в index.js (или где бы вы ни находились):

#!/usr/bin/env node
process.env.UV_THREADPOOL_SIZE = 128;

function main() {
   ...
}

Изменение: Я также написал сообщение в блоге об этом.

Ответ 2

У меня была такая же проблема. Это решается с помощью "agent: false" в опции запроса после прочтения этого обсуждения.

10/31/2017 Оригинальный ответ выше, кажется, не полностью решает проблему. Окончательное решение, которое мы нашли, - использовать опцию keepAlive в агенте. Например:

var pool = new https.Agent({ keepAlive: true });

function getJsonOptions(_url) {
    return {
        url: _url,
        method: 'GET',
        agent: pool,
        json: true
    };
}

Пул по умолчанию для узла, по-видимому, по умолчанию keepAlive = false, что вызывает создание нового соединения при каждом запросе. Если за короткий промежуток времени создается слишком много соединений, появляется указанная выше ошибка. Я предполагаю, что один или несколько маршрутизаторов на пути к сервису блокируют запрос соединения, вероятно, в подозрении на атаку Deny Of Service. В любом случае приведенный выше пример кода полностью решил нашу проблему.

Ответ 3

В инструменте запроса (https://github.com/request/request)

Поддержание активности http-соединения по умолчанию отключено.

Вам нужно установить option.forever = true, чтобы открыть эту функцию.