Comment déboguer l'erreur ECONNRESET dans Node.js?

288

J'exécute une application Express.js en utilisant Socket.io pour une webapp de chat et j'obtiens l'erreur suivante au hasard environ 5 fois pendant 24h. Le processus de nœud est enveloppé pour toujours et il redémarre immédiatement.

Le problème est que le redémarrage d'Express expulse mes utilisateurs de leurs chambres et personne ne veut ça.

Le serveur Web est mandaté par HAProxy. Il n'y a pas de problèmes de stabilité de socket, il suffit d'utiliser les transports websockets et flashsockets. Je ne peux pas reproduire cela exprès.

C'est l'erreur avec Node v0.10.11:

    events.js:72
            throw er; // Unhandled 'error' event
                  ^
    Error: read ECONNRESET     //alternatively it s a 'write'
        at errnoException (net.js:900:11)
        at TCP.onread (net.js:555:19)
    error: Forever detected script exited with code: 8
    error: Forever restarting script for 2 time

MODIFIER (2013-07-22)

Ajout du gestionnaire d'erreurs du client socket.io et du gestionnaire d'exceptions non capturé. Semble que celui-ci capture l'erreur:

    process.on('uncaughtException', function (err) {
      console.error(err.stack);
      console.log("Node NOT Exiting...");
    });

Je soupçonne donc que ce n'est pas un problème Socket.io mais une requête HTTP vers un autre serveur que je fais ou une connexion MySQL / Redis. Le problème est que la pile d'erreurs ne m'aide pas à identifier mon problème de code. Voici la sortie du journal:

    Error: read ECONNRESET
        at errnoException (net.js:900:11)
        at TCP.onread (net.js:555:19)

Comment savoir ce qui cause cela? Comment tirer le meilleur parti de l'erreur?

Ok, pas très verbeux mais voici le stacktrace avec Longjohn:

    Exception caught: Error ECONNRESET
    { [Error: read ECONNRESET]
      code: 'ECONNRESET',
      errno: 'ECONNRESET',
      syscall: 'read',
      __cached_trace__:
       [ { receiver: [Object],
           fun: [Function: errnoException],
           pos: 22930 },
         { receiver: [Object], fun: [Function: onread], pos: 14545 },
         {},
         { receiver: [Object],
           fun: [Function: fireErrorCallbacks],
           pos: 11672 },
         { receiver: [Object], fun: [Function], pos: 12329 },
         { receiver: [Object], fun: [Function: onread], pos: 14536 } ],
      __previous__:
       { [Error]
         id: 1061835,
         location: 'fireErrorCallbacks (net.js:439)',
         __location__: 'process.nextTick',
         __previous__: null,
         __trace_count__: 1,
         __cached_trace__: [ [Object], [Object], [Object] ] } }

Ici, je sers le fichier de politique de socket flash:

    net = require("net")
    net.createServer( (socket) =>
      socket.write("<?xml version=\"1.0\"?>\n")
      socket.write("<!DOCTYPE cross-domain-policy SYSTEM \"http://www.macromedia.com/xml/dtds/cross-domain-policy.dtd\">\n")
      socket.write("<cross-domain-policy>\n")
      socket.write("<allow-access-from domain=\"*\" to-ports=\"*\"/>\n")
      socket.write("</cross-domain-policy>\n")
      socket.end()
    ).listen(843)

Cela peut-il être la cause?

Samson
la source
3
@GottZ peut-être que cela peut aider (a parlé à une personne travaillant au sein du nœud js) gist.github.com/samsonradu/1b0c6feb438f5a53e30e . Je vais déployer le gestionnaire socket.error aujourd'hui et vous le faire savoir.
Samson
1
@Gottz que socket.error gère n'aide pas, mais process.on ('uncaughtException') intercepte l'erreur. Voici le fichier console.log de l'erreur: {[Error: read ECONNRESET] code: 'ECONNRESET', errno: 'ECONNRESET', syscall: 'read'}
Samson
1
ECONNRESET pourrait provenir d'un problème de réseau. Comme vous le savez, il est impossible de détecter toutes les exceptions lors des tests. Certains apparaîtront sur votre serveur de production. Vous devrez rendre votre serveur robuste. Vous pouvez gérer la suppression de session en utilisant Redis comme stockage. Cela rend vos sessions persistantes même après la panne de votre serveur de noeud.
user568109
1
Pourquoi est-ce lié à la suppression de session? Ils sont quand même gérés par Redis.
Samson
3
Vous avez au moins un socket TCP qui n'écoute pas le gestionnaire défini. Alors maintenant, il est temps de vérifier où celui-ci est: D
Moss

Réponses:

253

Vous l'avez peut-être déjà deviné: c'est une erreur de connexion.

"ECONNRESET" signifie que l'autre côté de la conversation TCP a brusquement fermé son extrémité de la connexion. Cela est probablement dû à une ou plusieurs erreurs de protocole d'application. Vous pouvez consulter les journaux du serveur API pour voir s'il se plaint de quelque chose.

Mais comme vous cherchez également un moyen de vérifier l'erreur et éventuellement de déboguer le problème, vous devriez jeter un œil à " Comment déboguer une erreur de blocage de socket dans NodeJS? ", Qui a été publiée sur stackoverflow en relation avec une question similaire.

Solution de développement rapide et sale :

Utilisez longjohn , vous obtenez de longues traces de pile qui contiendront les opérations asynchrones.

Solution propre et correcte : Techniquement, dans le nœud, chaque fois que vous émettez un 'error'événement et que personne ne l'écoute, il lance . Pour ne pas le jeter, mettez-y un auditeur et manipulez-le vous-même. De cette façon, vous pouvez enregistrer l'erreur avec plus d'informations.

Pour avoir un seul écouteur pour un groupe d'appels, vous pouvez utiliser des domaines et également détecter d'autres erreurs lors de l'exécution. Assurez-vous que chaque opération asynchrone liée à http (serveur / client) est dans un contexte de domaine différent par rapport aux autres parties du code, le domaine écoutera automatiquement les errorévénements et le propagera à son propre gestionnaire. Vous n'écoutez donc que ce gestionnaire et obtenez les données d'erreur. Vous obtenez également plus d'informations gratuitement.

MODIFIER (2013-07-22)

Comme je l'ai écrit ci-dessus:

"ECONNRESET" signifie que l'autre côté de la conversation TCP a brusquement fermé son extrémité de la connexion. Cela est probablement dû à une ou plusieurs erreurs de protocole d'application. Vous pouvez consulter les journaux du serveur API pour voir s'il se plaint de quelque chose.

Ce qui pourrait également être le cas: à des moments aléatoires, l'autre côté est surchargé et tue simplement la connexion en conséquence. Si tel est le cas, cela dépend de ce à quoi vous vous connectez exactement…

Mais une chose est sûre: vous avez en effet une erreur de lecture sur votre connexion TCP qui provoque l'exception. Vous pouvez le voir en regardant le code d'erreur que vous avez publié dans votre modification, ce qui le confirme.

e-sushi
la source
Cela ne doit pas signifier «brusquement fermé». Il résulte généralement de l'écriture sur une connexion que l'homologue avait déjà fermée normalement. Cela entraînera l'émission d'une TVD.
Marquis de Lorne
1
@EJP Il y avait une bonne raison pour laquelle j'ai écrit "brusquement". L'erreur (pas d'avertissement) indique que la connexion a été réinitialisée par l'homologue. Une connexion existante a été fermée de force par l'homologue distant. Une fermeture forcée est brutale car inattendue! (Cela se produit normalement si l'application homologue sur la machine distante est soudainement arrêtée, la machine est redémarrée ou l'application homologue a utilisé une "fermeture ferme" sur le socket distant. Cette erreur peut également se produire si une connexion a été interrompue en raison de l'activité "keep-alive" détecter une défaillance pendant qu'une ou plusieurs opérations sont en cours… ces opérations et les opérations suivantes échoueront.)
e-sushi
2
J'obtiens cette erreur lorsque j'envoie par lots une centaine d'appels API presque simultanément depuis le navigateur (Chrome) pour les tests. J'imagine que Chrome doit alors être surchargé et tuer certaines des connexions ... @Samson - qu'est-ce qui ne va pas avec le traitement de chaque demande dans son propre domaine et la capture des erreurs de domaine sans redémarrer le serveur?
supershnee
2
@supershnee Vous devez presque toujours redémarrer votre serveur après une exception non interceptée car vos données, votre application et node.js lui-même sont dans un état inconnu. Continuer après une exception met vos données en danger. Si vous souhaitez en savoir plus, consultez les documents de Node sur le processus ou les documents de Node sur les domaines .
c1moore
39

Un simple serveur TCP que j'avais pour servir le fichier de politique flash était à l'origine de cela. Je peux maintenant attraper l'erreur en utilisant un gestionnaire:

# serving the flash policy file
net = require("net")

net.createServer((socket) =>
  //just added
  socket.on("error", (err) =>
    console.log("Caught flash policy server socket error: ")
    console.log(err.stack)
  )

  socket.write("<?xml version=\"1.0\"?>\n")
  socket.write("<!DOCTYPE cross-domain-policy SYSTEM \"http://www.macromedia.com/xml/dtds/cross-domain-policy.dtd\">\n")
  socket.write("<cross-domain-policy>\n")
  socket.write("<allow-access-from domain=\"*\" to-ports=\"*\"/>\n")
  socket.write("</cross-domain-policy>\n")
  socket.end()
).listen(843)
Samson
la source
2
Y a-t-il un problème avec le code? Dois-je avoir vérifié si le socket est accessible en écriture avant d'écrire?
Samson
Doh, je n'ai pas vu que vous aviez déjà trouvé la solution avant de poster à peu près la même chose :) Quant à votre question cependant, même si vous vérifiez que le socket est accessible en écriture, il se peut que ce ne soit pas lorsque vous l'écrivez quelques microsecondes plus tard et serait toujours jeter une erreur, c'est donc "la voie" pour être sûr.
Joachim Isaksson
ok, et y a-t-il une issue sûre si cela? comme socket.close () dans le gestionnaire d'erreurs? parce que je pense que ma charge CPU augmente après ces erreurs (pas sûr)
Samson
2
J'ai toujours appelé socket.destroy()le gestionnaire d'erreurs pour m'assurer. Malheureusement, je ne trouve pas de documentation si elle est requise, mais elle n'émet pas d'erreur pour le faire.
Joachim Isaksson
socket.destroy () m'a sauvé la journée, quoi que ça marche !! Je vous remercie!
Firas Abd Alrahman
27

J'ai eu un problème similaire où les applications ont commencé à générer des erreurs après une mise à niveau de Node. Je crois que cela peut être retracé à la version Node v0.9.10 de cet article:

  • net: ne pas supprimer ECONNRESET (Ben Noordhuis)

Les versions précédentes n'erraient pas sur les interruptions du client. Une interruption de la connexion du client génère l'erreur ECONNRESET dans le nœud. Je crois que c'est une fonctionnalité prévue pour Node, donc le correctif (au moins pour moi) était de gérer l'erreur, ce que je pense que vous avez fait dans des exceptions non capturées. Bien que je le gère dans le gestionnaire net.socket.

Vous pouvez le démontrer:

Créez un serveur de socket simple et obtenez Node v0.9.9 et v0.9.10.

require('net')
    .createServer( function(socket) 
    {
           // no nothing
    })
    .listen(21, function()
     {
           console.log('Socket ON')
    })

Démarrez-le à l'aide de la v0.9.9, puis essayez de FTP sur ce serveur. J'utilise FTP et le port 21 uniquement parce que je suis sous Windows et que j'ai un client FTP, mais pas de client telnet à portée de main.

Du côté client, rompez simplement la connexion. (Je fais juste Ctrl-C)

Vous devriez voir PAS D'ERREUR lorsque vous utilisez Node v0.9.9 et ERREUR lorsque vous utilisez Node v.0.9.10 et plus.

En production, j'utilise la v.0.10. quelque chose et il donne toujours l'erreur. Encore une fois, je pense que cela est prévu et la solution consiste à gérer l'erreur dans votre code.

John Williams
la source
3
Merci, je l'ai cloué moi-même! Il est important de ne pas laisser les erreurs se propager à uncaughtException car cela rend toute l'application instable. Par exemple, après avoir détecté environ 10 erreurs ECONNRESET, le serveur ne répondait parfois pas (juste gelé et ne gérait aucune connexion)
Samson
Je connaissais également le changement de version du nœud qui ne supprimait plus l'erreur, mais vu tant de problèmes apparaissant et résolus pour chaque version, je préfère la dernière. J'utilise V0.10.13 maintenant btw
Samson
16

Eu le même problème aujourd'hui. Après quelques recherches, j'ai trouvé une --abort-on-uncaught-exceptionoption node.js très utile . Non seulement il fournit une trace de pile d'erreurs beaucoup plus verbeuse et utile, mais enregistre également le fichier principal en cas de plantage de l'application, ce qui permet un débogage supplémentaire.

Suzana_K
la source
4
bizarre qu'une nouvelle réponse à cette vieille question apparaisse pendant que je regarde - mais c'est super, merci
Point
13

J'étais confronté au même problème mais je l'ai atténué en plaçant:

server.timeout = 0;

avant server.listen. serverest un serveur HTTP ici. Le délai d'expiration par défaut est de 2 minutes selon la documentation de l' API .

Ashish Kaila
la source
5
Ce n'est pas une solution mais plutôt un quickfix qui va casser les choses sans lancer d'erreur.
Nishant Ghodke
9

Un autre cas possible (mais rare) pourrait être si vous avez des communications de serveur à serveur et que vous avez défini server.maxConnectionsune valeur très faible.

Dans le noyau lib net.js du nœud, il appellera clientHandle.close()ce qui provoquera également l'erreur ECONNRESET:

if (self.maxConnections && self._connections >= self.maxConnections) {
  clientHandle.close(); // causes ECONNRESET on the other end
  return;
}
happy_marmoset
la source
Excellent appel, mais la maxConnectionsvaleur par défaut est Infinity. Ce ne serait le cas (comme vous l'avez dit) que si vous avez explicitement outrepassé cette valeur.
Gajus
7

Oui, votre portion du fichier de stratégie peut définitivement provoquer le crash.

Pour répéter, ajoutez simplement un délai à votre code:

net.createServer( function(socket) 
{
    for (i=0; i<1000000000; i++) ;
    socket.write("<?xml version=\"1.0\"?>\n");

… Et utilisez telnetpour vous connecter au port. Si vous déconnectez telnet avant l'expiration du délai, vous obtiendrez un plantage (exception non interceptée) lorsque socket.write génère une erreur.

Pour éviter le plantage ici, ajoutez simplement un gestionnaire d'erreur avant de lire / écrire le socket:

net.createServer(function(socket)
{
    for(i=0; i<1000000000; i++);
    socket.on('error', function() { console.log("error"); });
    socket.write("<?xml version=\"1.0\"?>\n");
}

Lorsque vous essayez la déconnexion ci-dessus, vous obtenez simplement un message de journal au lieu d'un plantage.

Et lorsque vous avez terminé, n'oubliez pas de supprimer le retard.

Joachim Isaksson
la source
6

J'obtiens également une erreur ECONNRESET pendant mon développement, la façon dont je le résous est en n'utilisant pas nodemon pour démarrer mon serveur, il suffit de l'utiliser "node server.js"pour démarrer mon serveur a résolu mon problème.

C'est bizarre, mais cela a fonctionné pour moi, maintenant je ne revois plus jamais l'erreur ECONNRESET.

Andrew Lam
la source
4

J'ai également eu cette erreur et j'ai pu la résoudre après des jours de débogage et d'analyse:

ma solution

Pour moi, VirtualBox (pour Docker) était le problème. J'ai eu la redirection de port configurée sur ma machine virtuelle et l'erreur ne s'est produite que sur le port redirigé.

conclusions générales

Les observations suivantes peuvent vous faire économiser des jours de travail que j'ai dû investir:

  • Pour moi, le problème ne s'est produit que sur les connexions de localhost à localhost sur un port. -> vérifier la modification de l'une de ces constantes résout le problème.
  • Pour moi, le problème ne s'est produit que sur ma machine -> laisser quelqu'un d'autre l'essayer.
  • Pour moi, le problème ne s'est produit qu'après un certain temps et n'a pas pu être reproduit de manière fiable
  • Mon problème n'a pu être inspecté avec aucun des nœuds ou des outils (de débogage). -> ne perdez pas de temps sur ce

-> déterminez si quelque chose ne va pas avec votre réseau (paramètres), comme les VM, les pare-feu, etc., c'est probablement la cause du problème.

Waog
la source
2

J'ai résolu le problème simplement connectant à un autre réseau . C'est l'un des problèmes possibles.

Tel que discuté plus haut, ECONNRESET signifie que la conversation TCP a brusquement fermé son extrémité de la connexion.

Votre connexion Internet peut vous empêcher de vous connecter à certains serveurs. Dans mon cas, j'essayais de me connecter à mLab (service de base de données cloud qui héberge les bases de données MongoDB). Et mon FAI le bloque.

Yousef
la source
Celui-ci a fonctionné pour moi, mon code qui fonctionnait bien il y a quelques heures a soudainement cessé de fonctionner, il s'avère que le changement de réseau a causé le problème
Aklank Jain
2

J'avais résolu ce problème en:

  • Désactiver ma connexion wifi / ethernet et allumer.
  • J'ai tapé: npm updatedans le terminal pour mettre à jour npm.
  • J'ai essayé de me déconnecter de la session et de me reconnecter

Après cela, j'ai essayé la même commande npm et la bonne chose est qu'elle a fonctionné. Je n'étais pas sûr que ce soit aussi simple que cela.

J'utilise CENTOS 7

muhammad tayyab
la source
0

J'ai eu le même problème et il semble que la version Node.js était le problème.

J'ai installé la version précédente de Node.js (10.14.2) et tout allait bien en utilisant nvm (vous permet d'installer plusieurs versions de Node.js et de passer rapidement d'une version à une autre).

Ce n'est pas une solution «propre», mais elle peut vous servir temporairement.

Sylvain
la source
0

Je viens de comprendre cela, au moins dans mon cas d'utilisation.

J'obtenais ECONNRESET. Il s'est avéré que la façon dont mon client était configuré, il frappait le serveur avec un appel API une tonne de fois très rapidement - et il n'avait besoin de frapper le point de terminaison qu'une seule fois.

Lorsque j'ai corrigé cela, l'erreur avait disparu.

VikR
la source
-2

Essayez d'ajouter ces options à socket.io:

const options = { transports: ['websocket'], pingTimeout: 3000, pingInterval: 5000 };

J'espère que cela t'aidera !

sol404
la source