Nœud.js request module getting ETIMEDOUT et ESOCKETTIMEDOUT

je suis de l'analyse d'un grand nombre de liens avec l' demande module en parallèle avec la combinaison de l' async module.

j'ai remarqué beaucoup de ETIMEDOUT et ESOCKETTIMEDOUT erreurs bien que les liens soient accessibles et répondent assez rapidement en utilisant chrome.

j'ai limité le maxSockets 2 et timeout à 10000 dans la demande options. Je suis à l'aide de async.filterLimit() avec une limite de 2 pour réduire le parallélisme de 2 demande à chaque fois. Donc j'en ai 2 sockets, 2 la demande, et un délai d'attente de 10 secondes à attendre pour les en-têtes de réponse du serveur mais j'obtiens ces erreurs.

Ici;s de demande de configuration que j'utilise:

{
            ...
            pool: {
                maxSockets: 2
            },
            timeout: 10000
            ,
            time: true
            ...
        }

Voici l'extrait de code que j'utilise pour fecth links:

    var self = this;
    async.filterLimit(resources, 2, function(resource, callback) {
        request({
            uri: resource.uri
        }, function (error, response, body) {
            if (!error && response.statusCode === 200) {
                ...
            } else {
                self.emit('error', resource, error);
            }
            callback(...);
        })
    }, function(result) {
        callback(null, result);
    });

j'ai écouté l'événement d'erreur et je vois à chaque fois que le code d'erreur est ETIMEDOUT l'objet connect est soit true/false donc parfois c'est un délai de connexion et parfois ce n'est pas le cas (selon la requête docs)

mise à jour: J'ai décidé de stimuler l' maxSocketsInfinity donc aucune connexion ne sera raccrochée en raison du manque de sockets disponibles:

pool: {
    maxSockets: Infinity
}

afin de contrôler la bande passante j'ai implémenté un requestLoop méthode qui traite la requête avec un maxAttemps et retryDelay paramètres pour contrôler les requêtes:

async.filterLimit(resources, 10, function(resource, callback) {
        self.requestLoop({
            uri: resource.uri
        }, 100, 5000, function (error, response, body) {
                var fetched = false;
                if (!error) {
                    ...
                } else {
                    ....
                }
                callback(...);
            });
    }, function(result) {
        callback(null, result);
    });

Mise en place de requestLoop:

requestLoop = function(options, attemptsLeft, retryDelay, callback, lastError) {
    var self = this;
    if (attemptsLeft <= 0) {
        callback((lastError != null ? lastError : new Error('...')));
    } else {
        request(options, function (error, response, body) {
            var recoverableErrors = ['ESOCKETTIMEDOUT', 'ETIMEDOUT', 'ECONNRESET', 'ECONNREFUSED'];
            var e;
            if ((error && _.contains(recoverableErrors, error.code)) || (response && (500 <= response.statusCode && response.statusCode < 600))) {
                e = error ? new Error('...');
                e.code = error ? error.code : response.statusCode;
                setTimeout((function () {
                    self.requestLoop(options, --attemptsLeft, retryDelay, callback, e);
                }), retryDelay);
            } else if (!error && (200 <= response.statusCode && response.statusCode < 300)) {
                callback(null, response, body);
            } else if (error) {
                e = new Error('...');
                e.code = error.code;
                callback(e);
            } else {
                e = new Error('...');
                e.code = response.statusCode;
                callback(e);
            }
        });
    }
};

Donc cette somme: Boosté maxSocketsInfinity pour essayer de surmonter l'erreur de temporisation de la connexion des sockets - Implemnted requestLoop méthode de contrôle de la demande qui a échoué et maxAttemps ainsi que retryDelay de telles demandes - Il y a aussi le nombre maxium de requête concurrente défini par le nombre passé à async.filterLimit

je tiens à noter que j'ai également joué avec les paramètres de tout ici dans le but d'obtenir des erreurs free crawling mais jusqu'à présent les tentatives ont échoué ainsi.

toujours à la recherche d'aide pour résoudre ce problème problème.

UPDATE2: J'ai décidé de laisser tomber asynchrone.filtrlimit et créer mon propre mécanisme de limite. J'ai juste 3 variables pour m'aider à réaliser ceci:

pendingRequests - un tableau de requêtes qui retiendra toutes les requêtes (expliquera plus tard) activeRequests - nombre de demandes actives maxConcurrentRequests - nombre de requêtes simultanées maximales autorisées

dans le tableau pendingRequests, je pousse un objet complexe contenant une référence à la fonction requestLoop comme ainsi que le tableau d'arguments contenant les arguments à passer à la fonction de boucle:

self.pendingRequests.push({
                "arguments": [{
                    uri: resource.uri.toString()
                }, self.maxAttempts, function (error, response, body) {
                    if (!error) {
                        if (self.policyChecker.isMimeTypeAllowed((response.headers['content-type'] || '').split(';')[0]) &&
                            self.policyChecker.isFileSizeAllowed(body)) {
                            self.totalBytesFetched += body.length;
                            resource.content = self.decodeBuffer(body, response.headers["content-type"] || '', resource);
                            callback(null, resource);
                        } else {
                            self.fetchedUris.splice(self.fetchedUris.indexOf(resource.uri.toString()), 1);
                            callback(new Error('Fetch failed because a mime-type is not allowed or file size is bigger than permited'));
                        }
                    } else {
                        self.fetchedUris.splice(self.fetchedUris.indexOf(resource.uri.toString()), 1);
                        callback(error);
                    }
                    self.activeRequests--;
                    self.runRequest();
                }],
                "function": self.requestLoop
            });
            self.runRequest();

vous " remarquez l'appel à runRequest() à la fin. Cette fonction permet de gérer les requêtes et de les lancer quand il le peut tout en gardant le maximum activeRequests sous la limite de maxConcurrentRequests:

var self = this;
    process.nextTick(function() {
        var next;
        if (!self.pendingRequests.length || self.activeRequests >= self.maxConcurrentRequests) {
            return;
        }
        self.activeRequests++;
        next = self.pendingRequests.shift();
        next["function"].apply(self, next["arguments"]);
        self.runRequest();
    });

cela devrait résoudre toutes les erreurs de Timeouts, à travers mes tests tho, j'ai encore remarqué quelques timeouts dans des sites spécifiques que j'ai testé sur. Je ne peux pas être sûr à 100% sur cela, mais je pense que c'est dû à la nature du site qui supporte le serveur http en limitant au maximum les requêtes d'un utilisateur en vérifiant l'adresse ip et en retournant par conséquent quelques messages HTTP 400 pour éviter une éventuelle "attaque" sur le serveur.

31
demandé sur Jorayen 2016-02-14 04:08:41

2 réponses

Edit: doublon de https://stackoverflow.com/a/37946324/744276

par défaut, le noeud a 4 ouvriers pour résoudre les requêtes DNS. Si votre requête DNS prend du temps, les requêtes vont bloquer la phase DNS, et le symptôme est exactement ESOCKETTIMEDOUT ou ETIMEDOUT.

essayez d'augmenter la taille de votre pool de fils uv:

export UV_THREADPOOL_SIZE=128
node ...

ou index.js (ou partout où votre point d'entrée est):

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

function main() {
   ...
}

j'ai reproduit ce localement en ralentissant les réponses du serveur DNS en utilisant tc.

26
répondu Motiejus Jakštys 2017-05-23 11:47:22

j'ai trouvé que s'il y a trop de requêtes async, alors L'exception ESOCKETTIMEDOUT se produit sous linux. La solution de contournement que j'ai trouvé est de faire ceci:

paramétrage de ces options pour requêter(): agent: false, pool: {maxSockets: 100} Notez qu'après cela, le temps d'arrêt peut être mentant de sorte que vous pourriez avoir besoin de l'augmenter.

2
répondu cancerbero 2017-11-04 22:34:49