Come evitare l'arresto anomalo di node.js? try-catch non funziona


157

Dalla mia esperienza, un server php genererebbe un'eccezione al registro o alla fine del server, ma node.js semplicemente si blocca. Circondare il mio codice con un try-catch non funziona neanche dal momento che tutto viene fatto in modo asincrono. Vorrei sapere cosa fanno gli altri nei loro server di produzione.

Risposte:


132

Altre risposte sono davvero pazze, come puoi leggere nei documenti di Node su http://nodejs.org/docs/latest/api/process.html#process_event_uncaughtexception

Se qualcuno utilizza altre risposte dichiarate leggi Node Docs:

Si noti che uncaughtExceptionè un meccanismo molto grezzo per la gestione delle eccezioni e potrebbe essere rimosso in futuro

PM2

Prima di tutto, consiglio vivamente l'installazione PM2per Node.js. PM2 è davvero eccezionale nella gestione degli arresti anomali e nel monitoraggio delle app Node nonché nel bilanciamento del carico. PM2 avvia immediatamente l'app Node ogni volta che si arresta in modo anomalo, si arresta per qualsiasi motivo o anche al riavvio del server. Quindi, se un giorno anche dopo aver gestito il nostro codice, l'app si blocca, PM2 può riavviarlo immediatamente. Per ulteriori informazioni, Installazione ed esecuzione di PM2

Ora torniamo alla nostra soluzione per evitare che l'app stessa si blocchi.

Quindi, dopo averlo esaminato, ho finalmente trovato quello che il documento Node stesso suggerisce:

Non utilizzare uncaughtException, utilizzare domainscon clusterinvece. Se lo usi uncaughtException, riavvia l'applicazione dopo ogni eccezione non gestita!

DOMINIO con Cluster

Ciò che effettivamente facciamo è inviare una risposta di errore alla richiesta che ha provocato l'errore, lasciando che gli altri finissero il loro tempo normale e smettessero di ascoltare nuove richieste in quel lavoratore.

In questo modo, l'utilizzo del dominio va di pari passo con il modulo cluster, poiché il processo principale può eseguire il fork di un nuovo lavoratore quando un lavoratore rileva un errore. Vedi il codice qui sotto per capire cosa intendo

Utilizzando Domaine la resilienza di separare il nostro programma in più processi di lavoro utilizzando Cluster, possiamo reagire in modo più appropriato e gestire gli errori con una sicurezza molto maggiore.

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);
} 

Sebbene Domainsia in attesa di svalutazione e verrà rimosso quando la nuova sostituzione verrà fornita come indicato nella Documentazione di Node

Questo modulo è in attesa di deprecazione. Una volta finalizzata un'API sostitutiva, questo modulo sarà completamente deprecato. Gli utenti che devono assolutamente disporre delle funzionalità fornite dai domini possono fare affidamento su di esso per il momento, ma dovrebbero aspettarsi di dover migrare verso una soluzione diversa in futuro.

Ma fino a quando non verrà introdotta la nuova sostituzione, Domain with Cluster è l'unica buona soluzione suggerita dalla documentazione del nodo.

Per una comprensione Domaine una Clusterlettura approfondite

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

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

Grazie a @Stanley Luo per averci condiviso questa meravigliosa spiegazione approfondita su Cluster e Domini

Cluster e domini


9
Un avvertimento, Dominio è in attesa di deprecazione: collegamento . Il metodo suggerito, dai documenti Node, è usare cluster: link .
Paolo,

4
restart your application after every unhandled exception!Nel caso in cui 2000 utenti stiano utilizzando un server web nodo per lo streaming video e 1 utente abbia ricevuto un'eccezione, il riavvio non interromperà tutti gli altri utenti?
Vikas Bansal,

2
@VikasBansal Sì che sicuramente interrupt tutti gli utenti ed è per questo che è brutto da usare uncaughtExceptione l'uso Domaincon Clusterinvece così, se un utente si trova di fronte un'eccezione in modo che solo il suo filo viene rimosso dal cluster e ha creato uno nuovo per lui. E non è necessario riavviare anche il server Node. Dall'altro lato, se lo usi, uncaughtExceptiondevi riavviare il server ogni volta che uno dei tuoi utenti deve affrontare un problema. Quindi, usa Domain with Cluster.
Airy,

3
cosa dovremmo fare quando domainè completamente deprecato e rimosso?
Jas,

3
Ho trovato questo tutorial per coloro che non capiscono il concetto di clustere workers: sitepoint.com/…
Stanley Luo

81

Ho inserito questo codice nelle mie dichiarazioni obbligatorie e dichiarazioni globali:

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

per me va bene. l'unica cosa che non mi piace è che non ottengo tutte le informazioni che vorrei se lasciassi andare in crash.


45
Un avvertimento: questo metodo funziona bene, MA ricorda che TUTTE le risposte HTTP devono essere terminate correttamente. Ciò significa che se si verifica un'eccezione non rilevata durante la gestione di una richiesta HTTP, è comunque necessario chiamare end () sull'oggetto http.ServerResponse. Comunque lo attui dipende da te. In caso contrario, la richiesta verrà bloccata fino a quando il browser non si arrende. Se hai abbastanza di queste richieste, il server può esaurire la memoria.
BMiner,

3
@BMiner, potresti fornire una migliore implementazione? Ho notato questo problema (richiesta di sospensione), quindi non è meglio che riavviare il server utilizzando forevero qualcosa del genere.
pixelfreak,

6
Ciò richiede una spiegazione approfondita. So che fa schifo, ma ogni volta che si verifica un'eccezione non rilevata, il tuo server deve riavviare al più presto. In realtà, lo scopo dell'evento 'uncaughtException' è di usarlo come un'opportunità per inviare un'e-mail di avviso e quindi utilizzare process.exit (1); per arrestare il server. È possibile utilizzare per sempre o qualcosa del genere per riavviare il server. Qualsiasi richiesta HTTP in sospeso andrà in timeout e fallirà. I tuoi utenti saranno arrabbiati con te. Ma è la soluzione migliore. Perchè lo chiedi? Acquista stackoverflow.com/questions/8114977/...
BMiner

3
Per ottenere ulteriori informazioni dall'errore non rilevato, utilizzare: console.trace (err.stack);
Jesse Dunlap,

2
ATTENZIONE: La documentazione per node dice, in termini non incerti, che non dovresti mai farlo perché è follemente pericoloso: nodejs.org/api/process.html#process_event_uncaughtexception
Jeremy Logan,

28

Come accennato qui troverai error.stackun messaggio di errore più completo come il numero di riga che ha causato l'errore:

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

12

Provare supervisor

npm install supervisor
supervisor app.js

Oppure puoi installare foreverinvece.

Tutto ciò che farà è ripristinare il server quando si arresta in modo anomalo riavviandolo.

forever può essere utilizzato all'interno del codice per ripristinare con grazia tutti i processi che si arrestano in modo anomalo.

I foreverdocumenti hanno solide informazioni sulla gestione dell'uscita / errore a livello di codice.


9
Sicuramente questa non può essere la soluzione ... Nel tempo in cui il server è inattivo non può rispondere a nuove richieste in arrivo. Un'eccezione potrebbe essere generata dal codice dell'applicazione: il server deve rispondere con un errore 500, non solo crash e sperare che venga riavviato.
Ant Kutschera,

20
Quindi, come hacker, si potrebbe capire che devono inviare una semplice richiesta al server e perdere un parametro di richiesta - che porta a undef nel javascript che causa l'arresto anomalo di node.js. Con il tuo suggerimento, posso uccidere ripetutamente l'intero cluster. La risposta è far sì che l'applicazione non vada a buon fine, ovvero gestisca l'eccezione non rilevata e non si blocchi. e se il server gestisse molte sessioni voip? non è accettabile che si blocchi e si bruci e che tutte quelle sessioni esistenti muoiano con esso. i tuoi utenti sarebbero presto partiti.
Ant Kutschera,

5
@AntKutschera ecco perché le eccezioni dovrebbero essere casi eccezionali. Le eccezioni devono essere attivate solo in situazioni in cui non è possibile ripristinare e in cui il processo deve arrestarsi in modo anomalo. Dovresti usare altri mezzi per gestire questi casi eccezionali . Ma vedo il tuo punto. Dovresti fallire con grazia dove possibile. Vi sono tuttavia casi in cui continuare con uno stato corrotto farà più danni.
Raynos,

2
Sì, ci sono diverse scuole di pensiero qui. Il modo in cui l'ho imparato (Java piuttosto che Javascript) ci sono aspettative accettabili che dovresti aspettarti, conosciute forse come eccezioni di business, e poi ci sono eccezioni o errori di runtime, in cui non dovresti aspettarti di recuperare, come memoria insufficiente. Un problema con il non fallire con grazia è che alcune librerie che scrivo potrebbero dichiarare che genera un'eccezione nel caso di qualcosa di recuperabile, dire dove un utente potrebbe correggere il proprio input. nella tua app, non leggi i miei documenti e basta un arresto anomalo, dove l'utente potrebbe essere stato in grado di recuperare
Ant Kutschera,

1
@AntKutschera Ecco perché registriamo le eccezioni. È necessario analizzare i registri di produzione per le eccezioni comuni e capire se e come è possibile ripristinarli, anziché lasciare che il server si blocchi. Ho usato quella metodologia con PHP, Ruby on Rails e Node. Indipendentemente dal fatto che esca da un processo o meno, ogni volta che si genera un errore 500, si sta facendo un disservizio ai propri utenti. Questa non è una pratica specifica per JavaScript o nodo.
Eric Elliott,

7

L'uso di try-catch può risolvere gli errori non rilevati, ma in alcune situazioni complesse, non farà il lavoro giusto come la cattura della funzione asincrona. Ricorda che in Nodo, qualsiasi chiamata di funzione asincrona può contenere una potenziale operazione di arresto anomalo dell'app.

L'uso uncaughtExceptionè una soluzione alternativa ma è riconosciuto come inefficiente e probabilmente verrà rimosso nelle future versioni di Node, quindi non contare su di esso.

La soluzione ideale è utilizzare il dominio: http://nodejs.org/api/domain.html

Per assicurarti che l'app sia attiva e funzionante anche il tuo server si è bloccato, procedi nel seguente modo:

  1. utilizzare il cluster di nodi per fork più processi per core. Quindi, se un processo è morto, un altro processo verrà avviato automaticamente. Scopri: http://nodejs.org/api/cluster.html

  2. usa il dominio per catturare l'operazione asincrona invece di usare try-catch o non rilevati. Non sto dicendo che provare a catturare o non catturato sia una cattiva idea!

  3. usa per sempre / supervisore per monitorare i tuoi servizi

  4. aggiungi demone per eseguire l'app nodo: http://upstart.ubuntu.com

spero che questo ti aiuti!


4

Provare il modulo nodo pm2 è molto coerente e ha un'ottima documentazione. Gestione dei processi di produzione per le app Node.js con un bilanciamento del carico integrato. si prega di evitare uncaughtException per questo problema. https://github.com/Unitech/pm2


`riavvia l'applicazione dopo ogni eccezione non gestita!` Nel caso in cui 2000 utenti utilizzino un server web nodo per lo streaming video e 1 utente ottenga un'eccezione, il riavvio non interromperà tutti gli altri utenti?
Vikas Bansal,

Ero così felice quando ho scoperto PM2. ottimo software
Mladen Janjetovic

0

UncaughtException è "un meccanismo molto grezzo" (così vero) e i domini ora sono deprecati. Tuttavia, abbiamo ancora bisogno di un meccanismo per rilevare errori nei domini (logici). La Biblioteca:

https://github.com/vacuumlabs/yacol

può aiutarti a farlo. Con un po 'di scrittura extra puoi avere una buona semantica del dominio su tutto il tuo codice!


0

Funziona alla grande su restify:

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();
});
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.