¿Cómo evito que se bloquee node.js? try-catch no funciona

157

Desde mi experiencia, un servidor php lanzaría una excepción al registro o al final del servidor, pero node.js simplemente se bloquea. Rodear mi código con un try-catch tampoco funciona ya que todo se hace de forma asincrónica. Me gustaría saber qué hacen los demás en sus servidores de producción.

TiansHUo
fuente

Respuestas:

132

Otras respuestas son realmente una locura, ya que puede leer los documentos de Node en http://nodejs.org/docs/latest/api/process.html#process_event_uncaughtexception

Si alguien está usando otras respuestas declaradas, lea Node Docs:

Tenga en cuenta que uncaughtExceptiones un mecanismo muy burdo para el manejo de excepciones y puede eliminarse en el futuro

PM2

En primer lugar, recomendaría encarecidamente la instalación PM2de Node.js. PM2 es realmente excelente para manejar fallas y monitorear aplicaciones de Nodo, así como el equilibrio de carga. PM2 inicia inmediatamente la aplicación Node cada vez que se bloquea, se detiene por cualquier motivo o incluso cuando el servidor se reinicia. Entonces, si algún día, incluso después de administrar nuestro código, la aplicación se bloquea, PM2 puede reiniciarlo de inmediato. Para más información, Instalación y ejecución de PM2

Ahora volviendo a nuestra solución para evitar que la aplicación se bloquee.

Entonces, después de pasar, finalmente se me ocurrió lo que sugiere el documento Node:

No use uncaughtException, use domainscon en su clusterlugar. Si lo usa uncaughtException, reinicie su aplicación después de cada excepción no controlada.

DOMINIO con Cluster

Lo que realmente hacemos es enviar una respuesta de error a la solicitud que activó el error, al tiempo que permite que los demás finalicen en su tiempo normal y dejen de escuchar nuevas solicitudes en ese trabajador.

De esta manera, el uso del dominio va de la mano con el módulo de clúster, ya que el proceso maestro puede bifurcar a un nuevo trabajador cuando un trabajador encuentra un error. Vea el código a continuación para entender a qué me refiero

Al usar Domain, y la resistencia de separar nuestro programa en múltiples procesos de trabajo Cluster, podemos reaccionar de manera más apropiada y manejar los errores con mucha mayor seguridad.

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

Aunque Domainestá pendiente de desaprobación y se eliminará a medida que el nuevo reemplazo llegue como se indica en la documentación del nodo

Este módulo está pendiente de desuso. Una vez que se haya finalizado una API de reemplazo, este módulo quedará totalmente en desuso. Los usuarios que deben tener absolutamente la funcionalidad que proporcionan los dominios pueden confiar en ella por el momento, pero deben esperar migrar a una solución diferente en el futuro.

Pero hasta que no se introduzca el nuevo reemplazo, Domain with Cluster es la única buena solución que sugiere Node Documentation.

Para una comprensión profunda Domainy Clusterlectura

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

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

Gracias a @Stanley Luo por compartir esta maravillosa explicación detallada sobre Cluster y Dominios.

Cluster y Dominios

Aireado
fuente
9
Una palabra de advertencia, el dominio está pendiente de desuso: enlace . El método sugerido, de los documentos del nodo, es usar cluster: link .
Paul
44
restart your application after every unhandled exception!En el caso de que 2000 usuarios usen un servidor web de nodo para transmitir video y 1 usuario tenga una excepción, ¿reiniciar no interrumpirá a todos los demás usuarios?
Vikas Bansal
2
Sí @VikasBansal que seguramente interrupción todos los usuarios y es por eso que es malo a utilizar uncaughtExceptiony su uso Domaincon Clustercambio de modo, si un usuario se enfrenta a una excepción por lo que sólo el hilo se retira del cluster y crea uno nuevo para él. Y no necesita reiniciar su servidor Node también. Por otro lado, si lo usa uncaughtException, debe reiniciar su servidor cada vez que cualquiera de sus usuarios se enfrente a un problema. Por lo tanto, use Dominio con clúster.
Airy
3
¿Qué debemos hacer cuando domainestá totalmente en desuso y eliminado?
Jas
3
Encontré este tutorial para aquellos que no entienden el concepto clustery workers: sitepoint.com/…
Stanley Luo
81

Puse este código justo debajo de mis declaraciones obligatorias y declaraciones globales:

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

funciona para mi. Lo único que no me gusta es que no obtengo tanta información como si solo dejara que la cosa se estrellara.

hvgotcodes
fuente
45
Una advertencia: este método funciona bien, PERO recuerde que TODAS las respuestas HTTP deben terminarse correctamente. Eso significa que si se produce una excepción no detectada mientras maneja una solicitud HTTP, aún debe llamar a end () en el Objeto http.ServerResponse. Sin embargo, implementar esto depende de usted. Si no hace esto, la solicitud se bloqueará hasta que el navegador se dé por vencido. Si tiene suficientes de estas solicitudes, el servidor puede quedarse sin memoria.
BMiner
3
@BMiner, ¿podría proporcionar una mejor implementación? Noté este problema (solicitud bloqueada), así que esto realmente no es mejor que simplemente reiniciar el servidor usando forevero algo así.
pixelfreak
66
Esto requiere una explicación en profundidad. Sé que esto apesta, pero cada vez que ocurre una excepción no detectada, su servidor debe reiniciarse lo antes posible. Realmente, el propósito del evento 'uncaughtException' es usarlo como una oportunidad para enviar un correo electrónico de advertencia y luego usar process.exit (1); para apagar el servidor. Puede usar para siempre o algo así para reiniciar el servidor. Cualquier solicitud HTTP pendiente expirará y fallará. Tus usuarios se enojarán contigo. Pero, es la mejor solución. ¿Porque preguntas? Checkout stackoverflow.com/questions/8114977/…
BMiner
3
Para obtener más información del error no detectado, use: console.trace (err.stack);
Jesse Dunlap
2
ADVERTENCIA: La documentación para el nodo dice, en términos inequívocos, que nunca debe hacer esto, ya que es una locura peligrosa: nodejs.org/api/process.html#process_event_uncaughtexception
Jeremy Logan
28

Como se menciona aquí , encontrará error.stackun mensaje de error más completo, como el número de línea que causó el error:

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

Tratar supervisor

npm install supervisor
supervisor app.js

O puede instalar en su foreverlugar.

Todo esto hará que recuperes tu servidor cuando se bloquea reiniciando.

forever se puede usar dentro del código para recuperar con gracia cualquier proceso que se bloquee.

Los foreverdocumentos tienen información sólida sobre el manejo de salida / error mediante programación.

Raynos
fuente
9
Seguramente esta no puede ser la solución ... En el tiempo durante el cual el servidor está inactivo, no puede responder a las nuevas solicitudes entrantes. Se podría generar una excepción en el código de la aplicación: el servidor debe responder con un error 500, no solo bloquearse y esperar que se reinicie.
Ant Kutschera
20
Entonces, como pirata informático, uno podría darse cuenta de que necesita enviar una solicitud simple al servidor y omitir un parámetro de solicitud, lo que conduce a un undef en el JavaScript que hace que node.js se bloquee. Con su sugerencia, puedo matar a todo su grupo repetidamente. La respuesta es hacer que la aplicación falle con gracia, es decir, manejar la excepción no detectada y no bloquearse. ¿Qué pasa si el servidor maneja muchas sesiones de VoIP? no es aceptable que se bloquee y se queme y que todas esas sesiones existentes mueran con él. tus usuarios pronto se irían.
Ant Kutschera
55
@AntKutschera es por eso que las excepciones deberían ser casos excepcionales. Las excepciones solo deberían activarse en situaciones en las que no puede recuperarse y donde el proceso tiene que bloquearse. Debe utilizar otros medios para manejar estos casos excepcionales . Pero entiendo tu punto. Debes fallar con gracia donde sea posible. Sin embargo, hay casos en los que continuar con un estado corrupto hará más daño.
Raynos
2
Sí, hay diferentes escuelas de pensamiento aquí. De la forma en que lo aprendí (Java en lugar de Javascript) hay expectativas aceptables que debe esperar, conocidas quizás como excepciones comerciales, y luego hay excepciones o errores de tiempo de ejecución, en los que no debe esperar recuperarse, como si no hubiera memoria. Un problema con no fallar con gracia es que alguna biblioteca que escribo podría declarar que arroja una excepción en el caso de algo recuperable, digamos dónde un usuario podría corregir su entrada. en su aplicación, no lee mis documentos y simplemente se bloquea, donde el usuario podría haberse recuperado
Ant Kutschera
1
@AntKutschera Es por eso que registramos excepciones. Debería analizar sus registros de producción en busca de excepciones comunes, y determinar si podría recuperarse de ellos y cómo, en lugar de dejar que el servidor se bloquee. He usado esa metodología con PHP, Ruby on Rails y Node. Independientemente de si sales o no de un proceso, cada vez que arrojas un error 500, estás perjudicando a tus usuarios. Esta no es una práctica específica de JavaScript o de nodo.
Eric Elliott
7

El uso de try-catch puede resolver los errores no detectados, pero en algunas situaciones complejas, no funcionará correctamente, como atrapar la función asincrónica. Recuerde que en Node, cualquier llamada a la función asíncrona puede contener una posible operación de bloqueo de la aplicación.

El uso uncaughtExceptiones una solución alternativa, pero se reconoce que es ineficiente y es probable que se elimine en futuras versiones de Node, así que no cuente con ello.

La solución ideal es usar el dominio: http://nodejs.org/api/domain.html

Para asegurarse de que su aplicación esté en funcionamiento incluso si su servidor se ha bloqueado, siga estos pasos:

  1. use el clúster de nodo para bifurcar múltiples procesos por núcleo. Entonces, si un proceso murió, otro proceso se iniciará automáticamente. Echa un vistazo: http://nodejs.org/api/cluster.html

  2. use el dominio para capturar la operación asincrónica en lugar de usar try-catch o no capturado. ¡No digo que try-catch o no atrapado sea un mal pensamiento!

  3. use forever / supervisor para monitorear sus servicios

  4. agregue daemon para ejecutar su aplicación de nodo: http://upstart.ubuntu.com

¡espero que esto ayude!

Nam Nguyen
fuente
4

Intente con el módulo de nodo pm2, es muy consistente y tiene una excelente documentación. Administrador de procesos de producción para aplicaciones Node.js con un equilibrador de carga incorporado. evite la excepción no capturada para este problema. https://github.com/Unitech/pm2

Virendra Rathore
fuente
"reinicie su aplicación después de cada excepción no controlada".
Vikas Bansal
Estaba tan feliz cuando descubrí PM2. gran pieza de software
Mladen Janjetovic
0

UncaughtException es "un mecanismo muy burdo" (tan cierto) y los dominios están en desuso ahora. Sin embargo, todavía necesitamos algún mecanismo para detectar errores en los dominios (lógicos). La biblioteca:

https://github.com/vacuumlabs/yacol

puede ayudarte a hacer esto. ¡Con un poco de escritura adicional puede tener una buena semántica de dominio alrededor de su código!

Tomás Kulich
fuente
0

Funciona muy bien en 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();
});
PH Andrade
fuente