Wie debugge ich den Fehler ECONNRESET in Node.js?

288

Ich führe eine Express.js-Anwendung mit Socket.io für eine Chat-Webanwendung aus und erhalte den folgenden Fehler ungefähr fünfmal während 24 Stunden zufällig. Der Knotenprozess ist für immer eingebunden und startet sich sofort neu.

Das Problem ist, dass ein Neustart von Express meine Benutzer aus ihren Räumen wirft und niemand das will.

Der Webserver wird von HAProxy vertreten. Es gibt keine Probleme mit der Socket-Stabilität, nur die Verwendung von Websockets und Flashsockets-Transporten. Ich kann das nicht absichtlich reproduzieren.

Dies ist der Fehler bei 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

BEARBEITEN (22.07.2013)

Sowohl der Socket.io-Client-Fehlerbehandler als auch der nicht erfasste Ausnahmebehandler wurden hinzugefügt. Scheint, dass dieser den Fehler fängt:

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

Ich vermute also, dass es sich nicht um ein Socket.io-Problem handelt, sondern um eine HTTP-Anfrage an einen anderen Server oder eine MySQL / Redis-Verbindung. Das Problem ist, dass der Fehlerstapel mir nicht hilft, mein Codeproblem zu identifizieren. Hier ist die Protokollausgabe:

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

Woher weiß ich, was das verursacht? Wie kann ich mehr aus dem Fehler herausholen?

Ok, nicht sehr ausführlich, aber hier ist der Stacktrace mit 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] ] } }

Hier diene ich der Flash-Socket-Richtliniendatei:

    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)

Kann das die Ursache sein?

Samson
quelle
3
@GottZ Vielleicht kann dies helfen (sprach mit jemandem, der innerhalb des Knotens js arbeitet) gist.github.com/samsonradu/1b0c6feb438f5a53e30e . Ich werde den socket.error-Handler heute bereitstellen und Sie wissen lassen.
Samson
1
@Gottz die socket.error-Handles helfen nicht, aber process.on ('uncaughtException') fängt den Fehler ab. Hier ist das console.log des Fehlers: {[Fehler: ECONNRESET lesen] Code: 'ECONNRESET', Fehler: 'ECONNRESET', Systemaufruf: 'Lesen'}
Samson
1
ECONNRESET könnte von einem Netzwerkproblem herrühren. Wie Sie wissen, ist es unmöglich, beim Testen alle Ausnahmen zu erfassen. Einige werden auf Ihrem Produktionsserver angezeigt. Sie müssen Ihren Server robust machen. Sie können das Löschen der Sitzung durchführen, indem Sie Redis als Speicher verwenden. Dadurch bleiben Ihre Sitzungen auch nach dem Ausfall Ihres Knotenservers bestehen.
user568109
1
Warum hängt das mit dem Löschen von Sitzungen zusammen? Sie werden sowieso von Redis gehandhabt.
Samson
3
Sie haben mindestens einen TCP-Socket, der den Handler nicht eingestellt hat. Jetzt ist es Zeit zu überprüfen, wo sich dieser befindet: D
Moss

Antworten:

253

Sie haben es vielleicht schon erraten: Es ist ein Verbindungsfehler.

"ECONNRESET" bedeutet, dass die andere Seite der TCP-Konversation das Ende der Verbindung abrupt geschlossen hat. Dies ist höchstwahrscheinlich auf einen oder mehrere Anwendungsprotokollfehler zurückzuführen. Sie können in den API-Serverprotokollen nachsehen, ob sich etwas beschwert.

Da Sie aber auch nach einer Möglichkeit suchen, den Fehler zu überprüfen und das Problem möglicherweise zu debuggen, sollten Sie sich " So debuggen Sie einen Socket-Aufhängefehler in NodeJS? " Anschauen, der bei stackoverflow in Bezug auf eine ähnliche Frage veröffentlicht wurde.

Schnelle und schmutzige Lösung für die Entwicklung :

Verwenden Longjohn , erhalten Sie lange Stack - Traces, die die Asynchron - Operationen enthalten.

Saubere und korrekte Lösung : Technisch gesehen wird im Knoten immer dann'error' , wenn Sie ein Ereignis ausgeben und niemand darauf hört, es ausgelöst . Damit es nicht wirft, setzen Sie einen Hörer darauf und kümmern Sie sich selbst darum. Auf diese Weise können Sie den Fehler mit weiteren Informationen protokollieren.

Um einen Listener für eine Gruppe von Anrufen zu haben, können Sie Domänen verwenden und zur Laufzeit auch andere Fehler abfangen. Stellen Sie sicher, dass sich jede mit http (Server / Client) verbundene asynchrone Operation im Vergleich zu den anderen Teilen des Codes in einem anderen Domänenkontext befindet. Die Domäne hört die errorEreignisse automatisch ab und gibt sie an ihren eigenen Handler weiter. Sie hören also nur diesen Handler und erhalten die Fehlerdaten. Sie erhalten auch weitere Informationen kostenlos.

BEARBEITEN (22.07.2013)

Wie ich oben geschrieben habe:

"ECONNRESET" bedeutet, dass die andere Seite der TCP-Konversation das Ende der Verbindung abrupt geschlossen hat. Dies ist höchstwahrscheinlich auf einen oder mehrere Anwendungsprotokollfehler zurückzuführen. Sie können in den API-Serverprotokollen nachsehen, ob sich etwas beschwert.

Was auch der Fall sein könnte: Zu zufälligen Zeiten ist die andere Seite überlastet und unterbricht dadurch einfach die Verbindung. Wenn dies der Fall ist, hängt es davon ab, mit was Sie sich genau verbinden ...

Eines ist jedoch sicher: Sie haben tatsächlich einen Lesefehler in Ihrer TCP-Verbindung, der die Ausnahme verursacht. Sie können dies sehen, indem Sie sich den Fehlercode ansehen, den Sie in Ihrer Bearbeitung gepostet haben, der dies bestätigt.

E-Sushi
quelle
Es muss nicht "abrupt geschlossen" bedeuten. Dies ergibt sich normalerweise aus dem Schreiben in eine Verbindung, die der Peer bereits normal geschlossen hat. Dadurch wird eine RST ausgegeben.
Marquis von Lorne
1
@EJP Es gab einen guten Grund, warum ich "abrupt" schrieb. Der Fehler (keine Warnung) besagt, dass die Verbindung vom Peer zurückgesetzt wurde. Eine bestehende Verbindung wurde vom Remote-Peer zwangsweise geschlossen. Ein erzwungenes Schließen ist abrupt, da unerwartet! (Dies tritt normalerweise auf, wenn die Peer-Anwendung auf dem Remote-Computer plötzlich gestoppt wird, der Computer neu gestartet wird oder die Peer-Anwendung einen "Hard Close" am Remote-Socket verwendet. Dieser Fehler kann auch auftreten, wenn eine Verbindung aufgrund der "Keep-Alive" -Aktivität unterbrochen wurde Erkennen eines Fehlers, während eine oder mehrere Operationen ausgeführt werden… Diese Operationen und nachfolgende Operationen schlagen fehl.)
E-Sushi
2
Ich erhalte diesen Fehler, wenn ich ungefähr 100 API-Aufrufe fast gleichzeitig vom Browser (Chrome) zum Testen stapelweise sende. Ich stelle mir vor, dass Chrome dann überlastet werden und einige der Verbindungen beenden muss ... @Samson - Was ist falsch daran, jede Anforderung in einer eigenen Domäne zu verarbeiten und Domänenfehler abzufangen, ohne den Server neu zu starten?
Supershnee
2
@supershnee Sie sollten Ihren Server nach einer nicht erfassten Ausnahme fast immer neu starten, da sich Ihre Daten, Anwendungen und node.js selbst in einem unbekannten Zustand befinden. Wenn Sie nach einer Ausnahme fortfahren, besteht ein Risiko für Ihre Daten. Wenn Sie mehr erfahren möchten, lesen Sie die Dokumente von Node in Bearbeitung oder die Dokumente von Node in Domänen .
c1moore
39

Ein einfacher TCP-Server, den ich zum Bereitstellen der Flash-Richtliniendatei hatte, verursachte dies. Ich kann den Fehler jetzt mit einem Handler abfangen:

# 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
quelle
2
Stimmt etwas mit dem Code nicht? Sollte ich vor dem Schreiben überprüft haben, ob der Socket beschreibbar ist?
Samson
Doh, ich habe nicht gesehen, dass Sie die Lösung bereits gefunden haben, bevor ich so ziemlich das Gleiche gepostet habe :) Was Ihre Frage betrifft, kann es sein, dass Sie, selbst wenn Sie überprüfen, ob der Socket beschreibbar ist, dies nicht tun, wenn Sie Mikrosekunden später und später darauf schreiben würde immer noch einen Fehler werfen, also ist dies "der Weg", um sicher zu sein.
Joachim Isaksson
ok, und gibt es einen sicheren Ausweg, wenn dies? wie socket.close () im Fehlerbehandler? weil ich denke, dass meine CPU-Last nach diesen Fehlern zunimmt (nicht sicher)
Samson
2
Ich habe immer socket.destroy()den Fehlerbehandler aufgerufen , um sicherzugehen. Leider kann ich keine Dokumentation finden, ob dies erforderlich ist, aber es wird kein Fehler ausgegeben.
Joachim Isaksson
socket.destroy () hat mir den Tag gerettet, was auch immer es funktioniert !! Danke dir!
Firas Abd Alrahman
27

Ich hatte ein ähnliches Problem, bei dem Apps nach einem Upgrade von Node fehlerhaft wurden. Ich glaube, dass dies auf Node Release v0.9.10 dieses Artikels zurückgeführt werden kann:

  • net: ECONNRESET nicht unterdrücken (Ben Noordhuis)

Frühere Versionen würden bei Unterbrechungen durch den Client keine Fehler verursachen. Eine Unterbrechung der Verbindung vom Client löst den Fehler ECONNRESET im Knoten aus. Ich glaube, dies ist eine beabsichtigte Funktionalität für Node, daher bestand die Lösung (zumindest für mich) darin, den Fehler zu behandeln, den Sie meiner Meinung nach in nicht erfassten Ausnahmen getan haben. Obwohl ich es im net.socket-Handler erledige.

Sie können dies demonstrieren:

Erstellen Sie einen einfachen Socket-Server und holen Sie sich Node v0.9.9 und v0.9.10.

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

Starten Sie es mit v0.9.9 und versuchen Sie dann, eine FTP-Verbindung zu diesem Server herzustellen. Ich verwende FTP und Port 21 nur, weil ich unter Windows bin und einen FTP-Client habe, aber keinen Telnet-Client zur Hand.

Unterbrechen Sie dann von der Client-Seite einfach die Verbindung. (Ich mache nur Strg-C)

Bei Verwendung von Node v0.9.9 sollte NO ERROR und bei Verwendung von Node v.0.9.10 und höher KEIN FEHLER angezeigt werden.

In der Produktion verwende ich v.0.10. etwas und es gibt immer noch den Fehler. Auch hier denke ich, dass dies beabsichtigt ist und die Lösung darin besteht, den Fehler in Ihrem Code zu behandeln.

John Williams
quelle
3
Danke, ich habe es selbst geschafft! Es ist wichtig, dass Fehler nicht zu uncaughtException übertragen werden, da dadurch die gesamte App instabil wird. ZB nach etwa 10 ECONNRESET-Fehlern reagierte der Server manchmal nicht mehr (nur eingefroren und keine Verbindungen verarbeitet)
Samson
Ich wusste auch über die Änderung der Knotenversion Bescheid, die den Fehler nicht mehr unterdrückte, aber da bei jeder Version so viele Probleme auftauchten und behoben wurden, würde ich lieber die neueste Version wählen. Ich benutze jetzt V0.10.13 übrigens
Samson
16

Hatte heute das gleiche Problem. Nach einigen Recherchen fand ich eine sehr nützliche --abort-on-uncaught-exceptionnode.js Option . Es bietet nicht nur eine ausführlichere und nützlichere Ablaufverfolgung für Fehlerstapel, sondern speichert auch die Kerndatei beim Absturz der Anwendung und ermöglicht so ein weiteres Debuggen.

Suzana_K
quelle
4
seltsam, dass eine neue Antwort auf diese alte Frage auftaucht, wenn ich suche - aber das ist großartig, danke
Semikolon
13

Ich hatte das gleiche Problem, aber ich habe es gemildert, indem ich Folgendes platziert habe:

server.timeout = 0;

vorher server.listen. serverist hier ein HTTP-Server. Das Standardzeitlimit beträgt 2 Minuten gemäß API-Dokumentation .

Ashish Kaila
quelle
5
Dies ist keine Lösung, sondern ein Quickfix, der die Dinge kaputt macht, ohne einen Fehler auszulösen.
Nishant Ghodke
9

Ein anderer möglicher Fall (aber selten) könnte sein, wenn Sie Server-zu-Server-Kommunikation haben und eingestellt haben server.maxConnections und einen sehr niedrigen Wert eingestellt haben.

Im Kern lib lib.js des Knotens wird aufgerufen, clientHandle.close()was ebenfalls den Fehler ECONNRESET verursacht:

if (self.maxConnections && self._connections >= self.maxConnections) {
  clientHandle.close(); // causes ECONNRESET on the other end
  return;
}
happy_marmoset
quelle
Großartiger Anruf, aber der maxConnectionsStandardwert ist Infinity. Dies wäre (wie Sie sagten) nur dann der Fall, wenn Sie diesen Wert explizit überschrieben haben.
Gajus
7

Ja, Ihre Bereitstellung der Richtliniendatei kann definitiv zum Absturz führen.

Um dies zu wiederholen, fügen Sie Ihrem Code einfach eine Verzögerung hinzu:

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

… Und verwenden telnet, um eine Verbindung zum Port herzustellen. Wenn Sie Telnet trennen, bevor die Verzögerung abgelaufen ist, kommt es zu einem Absturz (nicht erfasste Ausnahme), wenn socket.write einen Fehler auslöst.

Um den Absturz hier zu vermeiden, fügen Sie einfach einen Fehlerbehandler hinzu, bevor Sie den Socket lesen / schreiben:

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

Wenn Sie versuchen, die oben genannte Verbindung zu trennen, wird anstelle eines Absturzes nur eine Protokollmeldung angezeigt.

Und wenn Sie fertig sind, denken Sie daran, die Verzögerung zu entfernen.

Joachim Isaksson
quelle
6

Ich erhalte auch einen ECONNRESET-Fehler während meiner Entwicklung. Die Art und Weise, wie ich ihn löse, besteht darin, dass ich meinen Server nicht mit nodemon starte. Nur "node server.js"zum Starten meines Servers wurde mein Problem behoben.

Es ist komisch, aber es hat bei mir funktioniert, jetzt sehe ich den ECONNRESET-Fehler nie wieder.

Andrew Lam
quelle
4

Ich hatte auch diesen Fehler und konnte ihn nach Tagen des Debuggens und Analysierens beheben:

meine Lösung

Für mich war VirtualBox (für Docker) das Problem. Ich hatte die Portweiterleitung auf meiner VM konfiguriert und der Fehler trat nur am weitergeleiteten Port auf.

allgemeine Schlussfolgerungen

Die folgenden Beobachtungen können Ihnen Arbeitstage ersparen, die ich investieren musste:

  • Bei mir trat das Problem nur bei Verbindungen von localhost zu localhost an einem Port auf. -> Überprüfen Sie, ob das Ändern einer dieser Konstanten das Problem löst.
  • Bei mir trat das Problem nur auf meinem Computer auf -> lassen Sie es von jemand anderem versuchen.
  • Für mich trat das Problem erst nach einer Weile auf und konnte nicht zuverlässig reproduziert werden
  • Mein Problem konnte mit keinem der Knoten- oder Express- (Debug-) Tools überprüft werden. -> Verschwenden Sie keine Zeit damit

-> Finden Sie heraus, ob etwas mit Ihrem Netzwerk (-Einstellungen) herumspielt, wie z. B. VMs, Firewalls usw., dies ist wahrscheinlich die Ursache des Problems.

Waog
quelle
2

Ich habe das Problem gelöst, indem ich mich einfach mit einem anderen Netzwerk verbunden habe . Das ist eines der möglichen Probleme.

Wie oben erläutert , ECONNRESET bedeutet , dass die TCP-Konversation das Ende der Verbindung abrupt geschlossen hat.

Ihre Internetverbindung blockiert möglicherweise die Verbindung zu einigen Servern. In meinem Fall habe ich versucht, eine Verbindung zu mLab (Cloud-Datenbankdienst, der MongoDB-Datenbanken hostet) herzustellen. Und mein ISP blockiert es.

Yousef
quelle
Dieser funktionierte für mich, mein Code, der vor ein paar Stunden einwandfrei funktionierte, funktionierte plötzlich nicht mehr, es stellte sich heraus, dass die Netzwerkänderung das Problem verursachte
einwandfrei Aklank Jain
2

Ich hatte dieses Problem gelöst durch:

  • Schalten Sie meine WLAN- / Ethernet-Verbindung aus und wieder ein.
  • Ich habe Folgendes eingegeben: npm updateim Terminal, um npm zu aktualisieren.
  • Ich habe versucht, mich von der Sitzung abzumelden und mich erneut anzumelden

Danach habe ich den gleichen npm-Befehl ausprobiert und das Gute war, dass es geklappt hat. Ich war mir nicht sicher, ob es so einfach ist.

Ich benutze CENTOS 7

Muhammad Tayyab
quelle
0

Ich hatte das gleiche Problem und es scheint, dass die Node.js-Version das Problem war.

Ich habe die vorherige Version von Node.js (10.14.2) installiert und mit nvm war alles in Ordnung (Sie können mehrere Versionen von Node.js installieren und schnell von einer Version zu einer anderen wechseln).

Es ist keine "saubere" Lösung, aber es kann Ihnen vorübergehend dienen.

Sylvain
quelle
0

Ich habe das gerade herausgefunden, zumindest in meinem Anwendungsfall.

Ich wurde ECONNRESET . Es stellte sich heraus, dass mein Client so eingerichtet war, dass er sehr schnell mit einem API-Aufruf auf den Server traf - und dass er den Endpunkt nur einmal erreichen musste.

Als ich das behoben habe, war der Fehler weg.

VikR
quelle
-2

Versuchen Sie, diese Optionen zu socket.io hinzuzufügen:

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

Ich hoffe, dies wird dir helfen !

sol404
quelle