web-dev-qa-db-fra.com

Node.js GET Request ETIMEDOUT & ESOCKETTIMEDOUT

J'utilise Node.js - module asynchrone et de requête pour explorer plus de 100 millions de sites Web et je continue à me heurter à des erreurs ESOCKETTIMEDOUT & ETIMEDOUT après quelques minutes.

Cela fonctionne à nouveau après avoir redémarré le script. Il ne semble pas y avoir de problème de limite de connexion, car je peux toujours faire resol4, resolverNs, resolverMx et aussi curl sans délai.

Voyez-vous un problème avec le code? ou un conseil? Je voudrais augmenter la concurrence async.queue () à au moins 1 000. Merci.

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's name server records
                            dns.resolveNs(task.domain, function (err, addresses) {
                                if (!err) {
                                    ns = addresses;
                                }
                                callback();
                            });
                        }, function (callback) {
                            // Resolves the domain's IPV4 addresses
                            dns.resolve4(task.domain, function (err, addresses) {
                                if (!err) {
                                    ip = addresses;
                                }
                                callback();
                            });
                        }, function (callback) {
                            // Resolves the domain's 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()
});

Et les limites du système sont assez élevées.

    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
16
Tan Hong Tat

Par défaut, Node has 4 travailleurs pour résoudre les requêtes DNS . Si votre requête DNS prend du temps, les requêtes se bloqueront sur la phase DNS et le symptôme est exactement ESOCKETTIMEDOUT ou ETIMEDOUT.

Essayez d'augmenter la taille de votre pool de threads uv:

export UV_THREADPOOL_SIZE=128
node ...

ou dans index.js (ou où que se trouve votre point d'entrée):

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

function main() {
   ...
}

Edit: j'ai également écrit un article de blog à ce sujet.

18
Motiejus Jakštys

J'ai eu le même problème. Il est résolu en utilisant "agent: false" dans l'option de requête après avoir lu cette discussion .

31/10/2017 La réponse d'origine ci-dessus ne semble pas résoudre complètement le problème. La solution finale que nous avons trouvée consiste à utiliser l'option keepAlive dans un agent. Par exemple:

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

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

Le pool par défaut du nœud semble avoir par défaut keepAlive = false, ce qui entraîne la création d'une nouvelle connexion à chaque demande. Lorsque trop de connexions sont créées en peu de temps, l'erreur ci-dessus apparaît. Je suppose qu'un ou plusieurs routeurs sur le chemin de la demande de connexion aux blocs de service, probablement en cas de suspicion d'attaque par refus de service. Dans tous les cas, l'exemple de code ci-dessus a complètement résolu notre problème.

6
Alex W