Wie verhindere ich, dass node.js abstürzt? try-catch funktioniert nicht


157

Nach meiner Erfahrung würde ein PHP-Server eine Ausnahme zum Protokoll oder zum Serverende auslösen, aber node.js stürzt einfach ab. Das Umgeben meines Codes mit einem Try-Catch funktioniert auch nicht, da alles asynchron ausgeführt wird. Ich würde gerne wissen, was alle anderen auf ihren Produktionsservern tun.

Antworten:


132

Andere Antworten sind wirklich verrückt, wie Sie in den eigenen Dokumenten von Node unter http://nodejs.org/docs/latest/api/process.html#process_event_uncaughtexception lesen können

Wenn jemand andere angegebene Antworten verwendet, lesen Sie die Knotendokumente:

Beachten Sie, dass dies uncaughtExceptionein sehr grober Mechanismus für die Ausnahmebehandlung ist und möglicherweise in Zukunft entfernt wird

PM2

Zuallererst würde ich die Installation PM2für sehr empfehlen Node.js. PM2 eignet sich hervorragend für den Umgang mit Abstürzen und die Überwachung von Node-Apps sowie für den Lastausgleich. PM2 startet die Node-App sofort, wenn sie abstürzt, aus irgendeinem Grund stoppt oder sogar wenn der Server neu gestartet wird. Wenn also eines Tages auch nach der Verwaltung unseres Codes die App abstürzt, kann PM2 sie sofort neu starten. Weitere Informationen finden Sie unter Installieren und Ausführen von PM2

Kommen wir nun zu unserer Lösung zurück, um zu verhindern, dass die App selbst abstürzt.

Nachdem ich es durchgearbeitet hatte, kam ich endlich auf das, was das Node-Dokument selbst vorschlägt:

Nicht verwenden uncaughtException, sondern domainsmit clusterverwenden. Wenn Sie verwenden uncaughtException, starten Sie Ihre Anwendung nach jeder nicht behandelten Ausnahme neu!

DOMAIN mit Cluster

Was wir tatsächlich tun, ist, eine Fehlerantwort auf die Anforderung zu senden, die den Fehler ausgelöst hat, während die anderen in ihrer normalen Zeit fertig werden und nicht mehr auf neue Anforderungen in diesem Worker warten.

Auf diese Weise geht die Domänennutzung Hand in Hand mit dem Cluster-Modul, da der Master-Prozess einen neuen Worker verzweigen kann, wenn ein Worker auf einen Fehler stößt. Sehen Sie sich den folgenden Code an, um zu verstehen, was ich meine

Durch die Verwendung Domainund die Widerstandsfähigkeit der Aufteilung unseres Programms in mehrere Arbeitsprozesse können Clusterwir angemessener reagieren und Fehler mit viel größerer Sicherheit behandeln.

var cluster = require('cluster');
var PORT = +process.env.PORT || 1337;

if(cluster.isMaster) 
{
   cluster.fork();
   cluster.fork();

   cluster.on('disconnect', function(worker) 
   {
       console.error('disconnect!');
       cluster.fork();
   });
} 
else 
{
    var domain = require('domain');
    var server = require('http').createServer(function(req, res) 
    {
        var d = domain.create();
        d.on('error', function(er) 
        {
            //something unexpected occurred
            console.error('error', er.stack);
            try 
            {
               //make sure we close down within 30 seconds
               var killtimer = setTimeout(function() 
               {
                   process.exit(1);
               }, 30000);
               // But don't keep the process open just for that!
               killtimer.unref();
               //stop taking new requests.
               server.close();
               //Let the master know we're dead.  This will trigger a
               //'disconnect' in the cluster master, and then it will fork
               //a new worker.
               cluster.worker.disconnect();

               //send an error to the request that triggered the problem
               res.statusCode = 500;
               res.setHeader('content-type', 'text/plain');
               res.end('Oops, there was a problem!\n');
           } 
           catch (er2) 
           {
              //oh well, not much we can do at this point.
              console.error('Error sending 500!', er2.stack);
           }
       });
    //Because req and res were created before this domain existed,
    //we need to explicitly add them.
    d.add(req);
    d.add(res);
    //Now run the handler function in the domain.
    d.run(function() 
    {
        //You'd put your fancy application logic here.
        handleRequest(req, res);
    });
  });
  server.listen(PORT);
} 

Die DomainAbschreibung steht jedoch noch aus und wird entfernt, sobald der neue Ersatz wie in der Dokumentation des Knotens angegeben geliefert wird

Dieses Modul steht noch aus. Sobald eine Ersatz-API fertiggestellt wurde, ist dieses Modul vollständig veraltet. Benutzer, die unbedingt über die von Domains bereitgestellten Funktionen verfügen müssen, können sich vorerst darauf verlassen, sollten jedoch damit rechnen, in Zukunft auf eine andere Lösung migrieren zu müssen.

Bis der neue Ersatz nicht eingeführt wird, ist Domain with Cluster die einzig gute Lösung, die die Knotendokumentation vorschlägt.

Zum gründlichen Verständnis Domainund ClusterLesen

https://nodejs.org/api/domain.html#domain_domain (Stability: 0 - Deprecated)

https://nodejs.org/api/cluster.html

Vielen Dank an @Stanley Luo für die Weitergabe dieser wunderbaren ausführlichen Erklärung zu Cluster und Domains

Cluster & Domains


9
Ein Wort der Warnung: Domain steht noch aus: Link . Die vorgeschlagene Methode aus den Knotendokumenten ist die Verwendung von cluster: link .
Paul

4
restart your application after every unhandled exception!Wenn 2000 Benutzer einen Knoten-Webserver zum Streamen von Videos verwenden und 1 Benutzer eine Ausnahme hat, werden durch einen Neustart nicht alle anderen Benutzer unterbrochen?
Vikas Bansal

2
@VikasBansal Ja, die sicherlich Interrupt all Benutzer und das ist , warum es schlecht ist zu verwenden , uncaughtExceptionund die Verwendung Domainmit Clusterstattdessen so, wenn ein Benutzer steht eine Ausnahme , so dass nur sein Faden von Cluster entfernt und neu für ihn geschaffen. Außerdem müssen Sie Ihren Node-Server nicht neu starten. Wenn Sie sich auf der anderen Seite befinden, müssen uncaughtExceptionSie Ihren Server jedes Mal neu starten, wenn ein Benutzer Probleme hat. Verwenden Sie also Domain with Cluster.
Luftig

3
Was sollen wir tun, wenn domaines vollständig veraltet und entfernt ist?
Jas

3
Fand dieses Tutorial für diejenigen, die das Konzept von clusterund nicht verstehen workers: sitepoint.com/…
Stanley Luo

81

Ich habe diesen Code direkt unter meine erforderlichen Anweisungen und globalen Deklarationen eingefügt:

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

funktioniert bei mir. Das einzige, was ich nicht mag, ist, dass ich nicht so viele Informationen bekomme, wie ich es tun würde, wenn ich das Ding einfach abstürzen lassen würde.


45
Ein Wort der Vorsicht: Diese Methode funktioniert gut, ABER denken Sie daran, dass ALLE HTTP-Antworten ordnungsgemäß beendet werden müssen. Das heißt, wenn während der Verarbeitung einer HTTP-Anforderung eine nicht erfasste Ausnahme auftritt, müssen Sie immer noch end () für das http.ServerResponse-Objekt aufrufen. Wie auch immer Sie dies implementieren, liegt bei Ihnen. Wenn Sie dies nicht tun, bleibt die Anfrage hängen, bis der Browser aufgibt. Wenn Sie genug von diesen Anforderungen haben, kann der Server nicht genügend Speicher haben.
BMiner

3
@BMiner, könnten Sie eine bessere Implementierung bereitstellen? Ich habe dieses Problem bemerkt (Anfrage hängt), daher ist dies wirklich nicht besser als nur den Server mit foreveroder so neu zu starten .
Pixelfreak

6
Dies erfordert eine ausführliche Erklärung. Ich weiß, das ist scheiße, aber wenn eine nicht erfasste Ausnahme auftritt, muss Ihr Server so schnell wie möglich neu starten. Der Zweck des Ereignisses 'uncaughtException' besteht darin, es als Gelegenheit zu nutzen, eine Warn-E-Mail zu senden und dann process.exit (1) zu verwenden. um den Server herunterzufahren. Sie können für immer oder so etwas verwenden, um den Server neu zu starten. Alle ausstehenden HTTP-Anforderungen treten eine Zeitüberschreitung auf und schlagen fehl. Ihre Benutzer werden sauer auf Sie sein. Aber es ist die beste Lösung. Warum fragst du? Kasse stackoverflow.com/questions/8114977/…
BMiner

3
Verwenden Sie: console.trace (err.stack), um weitere Informationen zu dem nicht erfassten Fehler zu erhalten.
Jesse Dunlap

2
WARNUNG: Die Dokumentation für Node besagt ohne Zweifel
Jeremy Logan

28

Wie hier erwähnt, finden Sie error.stackeine vollständigere Fehlermeldung, z. B. die Zeilennummer, die den Fehler verursacht hat:

process.on('uncaughtException', function (error) {
   console.log(error.stack);
});

12

Versuchen supervisor

npm install supervisor
supervisor app.js

Oder Sie können foreverstattdessen installieren .

Dies reicht lediglich aus, um Ihren Server wiederherzustellen, wenn er abstürzt, indem Sie ihn neu starten.

forever kann innerhalb des Codes verwendet werden, um alle Prozesse, die abstürzen, ordnungsgemäß wiederherzustellen.

Die foreverDokumente enthalten solide Informationen zur programmgesteuerten Behebung / Fehlerbehandlung.


9
Dies kann sicherlich nicht die Lösung sein ... In der Zeit, in der der Server ausfällt, kann er nicht auf neue eingehende Anfragen reagieren. Möglicherweise wird eine Ausnahme vom Anwendungscode ausgelöst. Der Server muss mit einem 500-Fehler antworten, nicht nur abstürzen und hoffen, dass er neu gestartet wird.
Ant Kutschera

20
Als Hacker könnte man also herausfinden, dass er eine einfache Anfrage an den Server senden und einen Anforderungsparameter verpassen muss - was zu einer Undef im Javascript führt, die zum Absturz von node.js führt. Mit Ihrem Vorschlag kann ich Ihren gesamten Cluster wiederholt töten. Die Antwort besteht darin, dass die Anwendung ordnungsgemäß fehlschlägt - dh die nicht erfasste Ausnahme behandelt und nicht abstürzt. Was ist, wenn der Server viele VoIP-Sitzungen abwickelt? Es ist nicht akzeptabel, dass es abstürzt und brennt und dass alle vorhandenen Sitzungen damit sterben. Ihre Benutzer würden bald verlassen.
Ant Kutschera

5
@AntKutschera deshalb sollten Ausnahmen Ausnahmefälle sein. Ausnahmen sollten nur in Situationen ausgelöst , wo Sie nicht können erholen und wo der Prozess hat zum Absturz bringen. Sie sollten andere Mittel verwenden, um diese Ausnahmefälle zu behandeln. Aber ich verstehe deinen Standpunkt. Sie sollten nach Möglichkeit elegant scheitern. Es gibt jedoch Fälle, in denen die Fortsetzung eines beschädigten Zustands mehr Schaden anrichtet.
Raynos

2
Ja, hier gibt es verschiedene Denkrichtungen. So wie ich es gelernt habe (Java statt Javascript), gibt es akzeptable Erwartungen, die Sie erwarten sollten, die möglicherweise als Geschäftsausnahmen bezeichnet werden, und dann gibt es Laufzeitausnahmen oder Fehler, bei denen Sie nicht erwarten sollten, dass sie wiederhergestellt werden, z. B. nicht genügend Speicher. Ein Problem, wenn ich nicht ordnungsgemäß versage, besteht darin, dass eine Bibliothek, die ich schreibe, möglicherweise deklariert, dass sie eine Ausnahme auslöst, wenn etwas wiederherstellbar ist, beispielsweise wenn ein Benutzer seine Eingabe korrigieren könnte. In Ihrer App lesen Sie meine Dokumente nicht und stürzen einfach ab, wo der Benutzer möglicherweise in der Lage war, sich zu erholen
Ant Kutschera

1
@AntKutschera Deshalb protokollieren wir Ausnahmen. Sie sollten Ihre Produktionsprotokolle auf häufige Ausnahmen analysieren und herausfinden, ob und wie Sie sie wiederherstellen können, anstatt den Server abstürzen zu lassen. Ich habe diese Methode mit PHP, Ruby on Rails und Node verwendet. Unabhängig davon, ob Sie einen Prozess beenden oder nicht, tun Sie Ihren Benutzern jedes Mal, wenn Sie einen 500-Fehler auslösen, einen schlechten Dienst. Dies ist keine JavaScript- oder knotenspezifische Vorgehensweise.
Eric Elliott

7

Die Verwendung von try-catch kann die nicht erfassten Fehler beheben, aber in einigen komplexen Situationen wird die Aufgabe nicht richtig ausgeführt, z. B. beim Abfangen der asynchronen Funktion. Denken Sie daran, dass in Node alle asynchronen Funktionsaufrufe einen potenziellen App-Absturz enthalten können.

Die Verwendung uncaughtExceptionist eine Problemumgehung, wird jedoch als ineffizient erkannt und wird wahrscheinlich in zukünftigen Versionen von Node entfernt. Verlassen Sie sich also nicht darauf.

Die ideale Lösung ist die Verwendung der Domain: http://nodejs.org/api/domain.html

Führen Sie die folgenden Schritte aus, um sicherzustellen, dass Ihre App funktioniert und auch Ihr Server abgestürzt ist:

  1. Verwenden Sie den Knotencluster, um mehrere Prozesse pro Kern zu verzweigen. Wenn also ein Prozess gestorben ist, wird ein anderer Prozess automatisch gestartet. Überprüfen Sie heraus: http://nodejs.org/api/cluster.html

  2. Verwenden Sie domain, um asynchrone Vorgänge abzufangen, anstatt try-catch oder uncaught zu verwenden. Ich sage nicht, dass Try-Catch oder Ungefangen ein schlechter Gedanke ist!

  3. Verwenden Sie für immer / Supervisor, um Ihre Dienste zu überwachen

  4. Fügen Sie einen Daemon hinzu, um Ihre Knoten-App auszuführen: http://upstart.ubuntu.com

hoffe das hilft!


4

Probieren Sie das pm2-Knotenmodul aus, es ist weit konsistent und verfügt über eine hervorragende Dokumentation. Produktionsprozessmanager für Node.js Apps mit integriertem Load Balancer. Bitte vermeiden Sie uncaughtException für dieses Problem. https://github.com/Unitech/pm2


`Starten Sie Ihre Anwendung nach jeder nicht behandelten Ausnahme neu!` Wenn 2000 Benutzer einen Knoten-Webserver zum Streamen von Videos verwenden und 1 Benutzer eine Ausnahme hat, werden durch einen Neustart nicht alle anderen Benutzer unterbrochen?
Vikas Bansal

Ich war so glücklich, als ich PM2 entdeckte. tolles Stück Software
Mladen Janjetovic

0

UncaughtException ist "ein sehr grober Mechanismus" (so wahr) und Domains sind jetzt veraltet. Wir benötigen jedoch noch einen Mechanismus, um Fehler in (logischen) Domänen abzufangen. Die Bibliothek:

https://github.com/vacuumlabs/yacol

kann Ihnen dabei helfen. Mit ein wenig mehr Schreiben können Sie eine schöne Domain-Semantik rund um Ihren Code haben!


0

Funktioniert hervorragend beim Restifizieren:

server.on('uncaughtException', function (req, res, route, err) {
  log.info('******* Begin Error *******\n%s\n*******\n%s\n******* End Error *******', route, err.stack);
  if (!res.headersSent) {
    return res.send(500, {ok: false});
  }
  res.write('\n');
  res.end();
});
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.