Ich habe gerade vor ein paar Tagen angefangen, node.js auszuprobieren. Ich habe festgestellt, dass der Knoten immer dann beendet wird, wenn ich eine nicht behandelte Ausnahme in meinem Programm habe. Dies unterscheidet sich von dem normalen Servercontainer, dem ich ausgesetzt war, bei dem nur der Worker-Thread stirbt, wenn nicht behandelte Ausnahmen auftreten und der Container die Anforderung weiterhin empfangen kann. Dies wirft einige Fragen auf:
- Ist
process.on('uncaughtException')
der einzig wirksame Weg, sich dagegen zu schützen? - Wird
process.on('uncaughtException')
die nicht behandelte Ausnahme auch während der Ausführung asynchroner Prozesse abgefangen? - Gibt es ein Modul, das bereits erstellt wurde (z. B. das Senden von E-Mails oder das Schreiben in eine Datei), das ich bei nicht erfassten Ausnahmen nutzen kann?
Ich würde mich über jeden Zeiger / Artikel freuen, der mir die gängigen Best Practices für den Umgang mit nicht erfassten Ausnahmen in node.js zeigt
try .. catch
und überprüfen, ob dies auch für alle Ihre Bibliotheken giltsetTimeout
odersetInterval
etwas Ähnliches irgendwo tief vergraben haben, das von Ihrem Code nicht erfasst werden kann.Antworten:
Update: Joyent hat jetzt einen eigenen Guide . Die folgenden Informationen sind eher eine Zusammenfassung:
Fehler sicher "werfen"
Im Idealfall möchten wir nicht erfasste Fehler so weit wie möglich vermeiden. Anstatt den Fehler buchstäblich auszulösen, können wir den Fehler abhängig von unserer Codearchitektur mit einer der folgenden Methoden sicher "auslösen":
Wenn bei synchronem Code ein Fehler auftritt, geben Sie den Fehler zurück:
Callback-basierte (dh. Asynchron) Code, das erste Argument des Rückrufs ist
err
, wenn ein Fehler passiert ,err
der Fehler ist, wenn ein Fehler dann nicht geschehenerr
istnull
. Alle anderen Argumente folgen demerr
Argument:Bei ereignisreichem Code, bei dem der Fehler überall auftreten kann, lösen Sie stattdessen das
error
Ereignis aus, anstatt den Fehler auszulösen :Fehler sicher "abfangen"
Manchmal gibt es jedoch immer noch Code, der irgendwo einen Fehler auslöst, der zu einer nicht erfassten Ausnahme und einem möglichen Absturz unserer Anwendung führen kann, wenn wir ihn nicht sicher abfangen. Abhängig von unserer Codearchitektur können wir eine der folgenden Methoden verwenden, um sie abzufangen:
Wenn wir wissen, wo der Fehler auftritt, können wir diesen Abschnitt in eine node.js-Domäne einschließen
Wenn wir wissen, wo der Fehler auftritt, ist synchroner Code und aus irgendeinem Grund keine Domänen verwenden können (möglicherweise alte Version des Knotens), können wir die try catch-Anweisung verwenden:
Achten Sie jedoch darauf, dass Sie keinen
try...catch
asynchronen Code verwenden, da ein asynchron ausgelöster Fehler nicht abgefangen wird:Wenn Sie
try..catch
in Verbindung mit asynchronem Code arbeiten möchten , können Sie beim Ausführen von Knoten 7.4 oder höherasync/await
Ihre asynchronen Funktionen nativ verwenden.Eine andere Sache, bei der Sie vorsichtig sein sollten,
try...catch
ist das Risiko, dass Sie Ihren Abschlussrückruftry
wie folgt in die Anweisung einschließen:Diese Vorgehensweise ist sehr einfach, da Ihr Code komplexer wird. Aus diesem Grund ist es am besten, entweder Domänen zu verwenden oder Fehler zurückzugeben, um (1) nicht erfasste Ausnahmen im asynchronen Code zu vermeiden. In Sprachen, die ein korrektes Threading anstelle des asynchronen Ereignismaschinenstils von JavaScript ermöglichen, ist dies weniger problematisch.
In dem Fall, dass ein nicht erfasster Fehler an einer Stelle auftritt, die nicht in eine Domäne eingeschlossen war, oder eine try catch-Anweisung, können wir unsere Anwendung mithilfe des
uncaughtException
Listeners nicht zum Absturz bringen (dies kann jedoch dazu führen, dass die Anwendung in einen unbekannten Zustand versetzt wird ):quelle
try catch
? Da würde ich das gerne mit Beweisen belegen. Das Synchronisierungsbeispiel wurde ebenfalls behoben.Es folgt eine Zusammenfassung und Kuration aus vielen verschiedenen Quellen zu diesem Thema, einschließlich Codebeispiel und Zitaten aus ausgewählten Blog-Posts. Die vollständige Liste der Best Practices finden Sie hier
Best Practices für die Fehlerbehandlung von Node.JS
Nummer 1: Verwenden Sie Versprechen für die asynchrone Fehlerbehandlung
TL; DR: Die Behandlung von asynchronen Fehlern im Rückrufstil ist wahrscheinlich der schnellste Weg zur Hölle (auch bekannt als die Pyramide des Untergangs). Das beste Geschenk, das Sie Ihrem Code machen können, ist die Verwendung einer seriösen Versprechensbibliothek, die eine sehr kompakte und vertraute Codesyntax wie Try-Catch bietet
Andernfalls: Der Node.JS-Rückrufstil, die Funktion (err, response) sind aufgrund der Mischung aus Fehlerbehandlung mit gelegentlichem Code, übermäßiger Verschachtelung und umständlichen Codierungsmustern ein vielversprechender Weg zu nicht wartbarem Code
Codebeispiel - gut
Codebeispiel Anti-Pattern - Fehlerbehandlung im Rückrufstil
Blog-Zitat: "Wir haben ein Problem mit Versprechungen" (Aus dem Blog pouchdb, Rang 11 für die Schlüsselwörter "Node Promises")
Nummer 2: Verwenden Sie nur das integrierte Fehlerobjekt
TL; DR: Es ist ziemlich üblich, Code zu sehen, der Fehler als Zeichenfolge oder als benutzerdefinierten Typ auslöst. Dies verkompliziert die Fehlerbehandlungslogik und die Interoperabilität zwischen Modulen. Unabhängig davon, ob Sie ein Versprechen ablehnen, eine Ausnahme auslösen oder einen Fehler ausgeben - die Verwendung des integrierten Fehlerobjekts Node.JS erhöht die Einheitlichkeit und verhindert den Verlust von Fehlerinformationen
Andernfalls: Wenn Sie ein Modul ausführen und sich nicht sicher sind, welche Art von Fehlern zurückkommt, ist es viel schwieriger, über die bevorstehende Ausnahme nachzudenken und sie zu behandeln. Die Verwendung von benutzerdefinierten Typen zur Beschreibung von Fehlern kann sogar zum Verlust kritischer Fehlerinformationen wie der Stapelverfolgung führen!
Codebeispiel - richtig machen
Codebeispiel Anti-Muster
Blog-Zitat: "Eine Zeichenfolge ist kein Fehler" (Aus dem Blog-Gedanken, Rang 6 für die Schlüsselwörter "Node.JS-Fehlerobjekt")
Nummer 3: Unterscheiden Sie Betriebs- und Programmierfehler
TL; DR: Betriebsfehler (z. B. API hat eine ungültige Eingabe erhalten) beziehen sich auf bekannte Fälle, in denen die Auswirkungen des Fehlers vollständig verstanden werden und sorgfältig behandelt werden können. Auf der anderen Seite bezieht sich ein Programmiererfehler (z. B. der Versuch, eine undefinierte Variable zu lesen) auf unbekannte Codefehler, die einen ordnungsgemäßen Neustart der Anwendung erfordern
Andernfalls: Sie können die Anwendung jederzeit neu starten, wenn ein Fehler auftritt. Warum sollten Sie jedoch ~ 5000 Online-Benutzer aufgrund eines geringfügigen und vorhergesagten Fehlers (Betriebsfehler) im Stich lassen? Das Gegenteil ist ebenfalls nicht ideal - das Aufrechterhalten der Anwendung, wenn ein unbekanntes Problem (Programmiererfehler) aufgetreten ist, kann zu unvorhergesehenem Verhalten führen. Die Unterscheidung der beiden ermöglicht es, taktvoll zu handeln und einen ausgewogenen Ansatz anzuwenden, der auf dem gegebenen Kontext basiert
Codebeispiel - richtig machen
Codebeispiel - Markieren eines Fehlers als betriebsbereit (vertrauenswürdig)
Blog-Zitat : "Andernfalls riskieren Sie den Status" (Aus dem Blog debugable, Rang 3 für die Schlüsselwörter "Node.JS nicht gefangene Ausnahme")
Nummer 4: Behandeln Sie Fehler zentral, jedoch nicht innerhalb der Middleware
TL; DR: Fehlerbehandlungslogik wie E-Mail an den Administrator und Protokollierung sollte in einem dedizierten und zentralisierten Objekt gekapselt sein, das alle Endpunkte (z. B. Express-Middleware, Cron-Jobs, Komponententests) aufrufen, wenn ein Fehler auftritt.
Andernfalls: Wenn Fehler nicht an einem einzigen Ort behandelt werden, führt dies zu einer Duplizierung des Codes und möglicherweise zu Fehlern, die nicht ordnungsgemäß behandelt werden
Codebeispiel - ein typischer Fehlerfluss
Blog-Zitat: "Manchmal können niedrigere Ebenen nichts Nützliches tun, als den Fehler an ihren Anrufer weiterzugeben" (Aus dem Blog Joyent, Rang 1 für die Schlüsselwörter "Node.JS-Fehlerbehandlung")
Nummer 5: Dokument-API-Fehler mit Swagger
TL; DR: Lassen Sie Ihre API-Aufrufer wissen, welche Fehler möglicherweise auftreten, damit sie diese nachdenklich behandeln können, ohne abzustürzen. Dies erfolgt normalerweise mit REST-API-Dokumentationsframeworks wie Swagger
Andernfalls: Ein API-Client entscheidet sich möglicherweise nur zum Absturz und Neustart, weil er einen Fehler erhalten hat, den er nicht verstehen konnte. Hinweis: Der Aufrufer Ihrer API könnten Sie sein (sehr typisch in einer Microservices-Umgebung).
Blog-Zitat: "Sie müssen Ihren Anrufern mitteilen, welche Fehler auftreten können" (Aus dem Blog Joyent, Rang 1 für die Schlüsselwörter "Node.JS-Protokollierung")
Nummer 6: Schließen Sie den Prozess elegant, wenn ein Fremder in die Stadt kommt
TL; DR: Wenn ein unbekannter Fehler auftritt (ein Entwicklerfehler, siehe Best Practice Nr. 3), besteht Unsicherheit über den Zustand der Anwendung. Eine gängige Praxis schlägt vor, den Prozess sorgfältig mit einem Neustart-Tool wie Forever und PM2 neu zu starten
Andernfalls: Wenn eine unbekannte Ausnahme abgefangen wird, befindet sich möglicherweise ein Objekt in einem fehlerhaften Zustand (z. B. ein Ereignisemitter, der global verwendet wird und aufgrund eines internen Fehlers keine Ereignisse mehr auslöst), und alle zukünftigen Anforderungen können fehlschlagen oder sich verrückt verhalten
Codebeispiel - Entscheidung, ob abgestürzt werden soll
Blog-Zitat: "Es gibt drei Denkschulen zur Fehlerbehandlung" (Aus dem Blog jsrecipes)
Nummer 7: Verwenden Sie einen ausgereiften Logger, um die Sichtbarkeit von Fehlern zu erhöhen
TL; DR: Eine Reihe ausgereifter Protokollierungswerkzeuge wie Winston, Bunyan oder Log4J beschleunigen die Fehlererkennung und das Verständnis. Vergessen Sie also console.log.
Andernfalls: Durchblättern von console.logs oder manuell durch unordentliche Textdateien ohne Abfrage von Tools oder eines anständigen Protokollbetrachters können Sie bis spät in die Arbeit beschäftigt sein
Codebeispiel - Winston Logger in Aktion
Blog-Zitat: "Lassen Sie uns einige Anforderungen identifizieren (für einen Logger):" (Aus dem Blog-Strongblog)
Nummer 8: Erkennen Sie Fehler und Ausfallzeiten mithilfe von APM-Produkten
TL; DR: Überwachungs- und Leistungsprodukte (auch bekannt als APM) messen Ihre Codebasis oder API proaktiv, damit sie fehlende Fehler, Abstürze und langsame Teile automatisch auf magische Weise hervorheben können
Andernfalls: Sie werden möglicherweise große Anstrengungen unternehmen, um die API-Leistung und Ausfallzeiten zu messen. Wahrscheinlich werden Sie nie wissen, welche Codeteile im realen Szenario am langsamsten sind und wie sich diese auf die UX auswirken
Blog-Zitat: "APM-Produktsegmente" (Aus dem Blog Yoni Goldberg)
Das Obige ist eine verkürzte Version - siehe hier mehr Best Practices und Beispiele
quelle
Sie können nicht erfasste Ausnahmen abfangen, aber es ist von begrenztem Nutzen. Siehe http://debuggable.com/posts/node-js-dealing-with-uncaught-exceptions:4c933d54-1428-443c-928d-4e1ecbdd56cb
monit
,forever
oderupstart
kann verwendet werden, um den Knotenprozess neu zu starten, wenn er abstürzt. Ein ordnungsgemäßes Herunterfahren ist das Beste, auf das Sie hoffen können (z. B. Speichern aller speicherinternen Daten im nicht erfassten Ausnahmebehandler).quelle
Error
die Rückgabe von wird der Rückgabewert polymorph, wodurch die Semantik der Funktion unnötig durcheinander gebracht wird. Außerdem tauchen von 0 bereits in JavaScript , indem sie behandeltInfinity
,-Infinity
oder dieNaN
Werte , wotypeof === 'number'
. Sie können mit überprüft werden!isFinite(value)
. Im Allgemeinen würde ich empfehlen, niemals einen Fehler von einer Funktion zurückzugeben. Besser in Bezug auf Code-Lesbarkeit und Wartung, um einen speziellen nicht polymorphen Wert mit konsistenter Semantik zu werfen oder zurückzugeben.nodejs-Domänen sind die aktuellste Methode zur Behandlung von Fehlern in nodejs. Domänen können sowohl Fehler- / andere Ereignisse als auch traditionell ausgelöste Objekte erfassen. Domänen bieten auch Funktionen zum Behandeln von Rückrufen mit einem Fehler, der als erstes Argument über die Intercept-Methode übergeben wird.
Wie bei der normalen Fehlerbehandlung im Try / Catch-Stil ist es normalerweise am besten, Fehler auszulösen, wenn sie auftreten, und Bereiche auszublenden, in denen Sie Fehler davon abhalten möchten, den Rest des Codes zu beeinflussen. Die Möglichkeit, diese Bereiche zu "blockieren", besteht darin, domain.run mit einer Funktion als Block isolierten Codes aufzurufen.
Im synchronen Code reicht das oben Genannte aus: Wenn ein Fehler auftritt, lassen Sie ihn entweder durchwerfen oder Sie fangen ihn ab und behandeln ihn dort, wobei Sie alle Daten zurücksetzen, die Sie zurücksetzen müssen.
Wenn der Fehler bei einem asynchronen Rückruf auftritt, müssen Sie entweder in der Lage sein, das Rollback von Daten (gemeinsam genutzter Status, externe Daten wie Datenbanken usw.) vollständig zu verarbeiten. ODER Sie müssen etwas festlegen, um anzuzeigen, dass eine Ausnahme aufgetreten ist. Wo immer Sie sich für dieses Flag interessieren, müssen Sie warten, bis der Rückruf abgeschlossen ist.
Ein Teil des obigen Codes ist hässlich, aber Sie können Muster für sich selbst erstellen, um ihn schöner zu machen, z.
UPDATE (2013-09):
Oben, verwende ich eine Zukunft , die impliziert Fasern Semantik , die Sie auf Futures in-line warten lassen. Auf diese Weise können Sie für alles traditionelle Try-Catch-Blöcke verwenden - was meiner Meinung nach der beste Weg ist. Sie können dies jedoch nicht immer tun (dh im Browser) ...
Es gibt auch Futures, für die keine Fasersemantik erforderlich ist (die dann mit normalem, browserartigem JavaScript funktioniert). Diese können als Futures, Versprechen oder Aufgeschobene bezeichnet werden (ich werde von nun an nur noch auf Futures verweisen). Mit einfachen JavaScript-Futures-Bibliotheken können Fehler zwischen Futures übertragen werden. Nur einige dieser Bibliotheken ermöglichen die korrekte Handhabung einer geworfenen Zukunft. Seien Sie also vorsichtig.
Ein Beispiel:
Dies ahmt einen normalen Try-Catch nach, obwohl die Teile asynchron sind. Es würde drucken:
Beachten Sie, dass '3' nicht ausgegeben wird, da eine Ausnahme ausgelöst wurde, die diesen Fluss unterbricht.
Schauen Sie sich die Versprechen von Bluebird an:
Beachten Sie, dass ich nicht viele andere Bibliotheken als diese gefunden habe, die ausgelöste Ausnahmen richtig behandeln. jQuery hat es zum Beispiel nicht aufgeschoben - der "Fail" -Handler würde niemals die Ausnahme als "Dann" -Handler auslösen, was meiner Meinung nach ein Deal Breaker ist.
quelle
Ich habe kürzlich darüber unter http://snmaynard.com/2012/12/21/node-error-handling/ geschrieben . Eine neue Funktion von Node in Version 0.8 sind Domänen, mit denen Sie alle Formen der Fehlerbehandlung in einem einfacheren Verwaltungsformular kombinieren können. Sie können darüber in meinem Beitrag lesen.
Sie können auch so etwas wie Bugsnag verwenden , um Ihre nicht erfassten Ausnahmen zu verfolgen und per E-Mail, Chatroom benachrichtigt zu werden oder ein Ticket für eine nicht erfasste Ausnahme erstellen zu lassen (ich bin der Mitbegründer von Bugsnag).
quelle
Ich möchte nur hinzufügen, dass die Step.js-Bibliothek Ihnen hilft, Ausnahmen zu behandeln, indem Sie sie immer an die nächste Schrittfunktion übergeben. Daher können Sie als letzten Schritt eine Funktion haben, die in den vorherigen Schritten nach Fehlern sucht. Dieser Ansatz kann Ihre Fehlerbehandlung erheblich vereinfachen.
Unten ist ein Zitat von der Github-Seite:
Darüber hinaus können Sie mit Step die Ausführung von Skripten steuern, um als letzten Schritt einen Bereinigungsabschnitt zu erhalten. Wenn Sie beispielsweise ein Build-Skript in Node schreiben und angeben möchten, wie lange das Schreiben gedauert hat, können Sie dies im letzten Schritt tun (anstatt zu versuchen, den letzten Rückruf auszuloten).
quelle
Ein Fall, in dem die Verwendung eines Try-Catch angemessen sein könnte, ist die Verwendung einer forEach-Schleife. Es ist synchron, aber gleichzeitig können Sie nicht einfach eine return-Anweisung im inneren Bereich verwenden. Stattdessen kann ein Try-and-Catch-Ansatz verwendet werden, um ein Fehlerobjekt im entsprechenden Bereich zurückzugeben. Erwägen:
Es ist eine Kombination der oben von @balupton beschriebenen Ansätze.
quelle
Nachdem ich diesen Beitrag vor einiger Zeit gelesen hatte, fragte ich mich, ob es sicher ist, Domänen für die Ausnahmebehandlung auf API- / Funktionsebene zu verwenden. Ich wollte sie verwenden, um den Code für die Ausnahmebehandlung in jeder von mir geschriebenen asynchronen Funktion zu vereinfachen. Ich befürchtete, dass die Verwendung einer neuen Domäne für jede Funktion einen erheblichen Overhead verursachen würde. Meine Hausaufgaben scheinen darauf hinzudeuten, dass es nur minimalen Overhead gibt und dass die Leistung bei Domains in einigen Situationen tatsächlich besser ist als bei Try Catch.
http://www.lighthouselogic.com/#/using-a-new-domain-for-each-async-function-in-node/
quelle
Das Abfangen von Fehlern wurde hier sehr gut besprochen, aber es lohnt sich, daran zu denken, die Fehler irgendwo abzumelden, damit Sie sie anzeigen und Fehler beheben können.
Bunyan ist ein beliebtes Protokollierungsframework für NodeJS - es unterstützt das Schreiben an eine Reihe verschiedener Ausgabeorte, was es für das lokale Debuggen nützlich macht, solange Sie console.log vermeiden. In der Fehlerbehandlungsroutine Ihrer Domain können Sie den Fehler in eine Protokolldatei ausspucken.
Dies kann zeitaufwändig werden, wenn Sie viele Fehler und / oder Server überprüfen müssen. Es kann sich daher lohnen, ein Tool wie Raygun (Haftungsausschluss, ich arbeite bei Raygun) zu verwenden, um Fehler zu gruppieren - oder beide zusammen zu verwenden. Wenn Sie sich für Raygun als Tool entschieden haben, ist die Einrichtung ebenfalls recht einfach
Wenn Sie ein Tool wie PM2 oder für immer verwenden, sollte Ihre App in der Lage sein, ohne größere Probleme abzustürzen, sich abzumelden und neu zu starten.
quelle
Wenn Sie Dienste in Ubuntu (Upstart) verwenden möchten: Knoten als Dienst in Ubuntu 11.04 mit upstart, monit und forever.js
quelle