Como evito que o node.js falhe? try-catch não funciona

157

Pela minha experiência, um servidor php lançaria uma exceção no log ou no final do servidor, mas o node.js simplesmente trava. Cercar meu código com um try-catch também não funciona, pois tudo é feito de forma assíncrona. Gostaria de saber o que todos os outros fazem em seus servidores de produção.

TiansHUo
fonte

Respostas:

132

Outras respostas são realmente insanas, como você pode ler nos próprios documentos do Node em http://nodejs.org/docs/latest/api/process.html#process_event_uncaughtexception

Se alguém estiver usando outras respostas indicadas, leia Node Docs:

Observe que esse uncaughtExceptioné um mecanismo muito bruto para manipulação de exceções e pode ser removido no futuro

PM2

Primeiro de tudo, eu recomendo a instalação PM2para Node.js. O PM2 é realmente ótimo no tratamento de falhas e no monitoramento de aplicativos do Nó, bem como no balanceamento de carga. O PM2 inicia imediatamente o aplicativo Node sempre que ele falha, para por qualquer motivo ou mesmo quando o servidor é reiniciado. Portanto, se algum dia, mesmo depois de gerenciar nosso código, o aplicativo travar, o PM2 poderá reiniciá-lo imediatamente. Para obter mais informações, Instalando e executando o PM2

Agora voltando à nossa solução para impedir que o próprio aplicativo falhe.

Então, depois de prosseguir, finalmente cheguei ao que o próprio documento do Node sugere:

Não use uncaughtException, use domainscom em clustervez disso. Se você usar uncaughtException, reinicie o aplicativo após cada exceção não tratada!

DOMAIN com Cluster

Na verdade, o que fazemos é enviar uma resposta de erro à solicitação que acionou o erro, enquanto os outros terminam no horário normal e parar de ouvir novas solicitações nesse trabalhador.

Dessa maneira, o uso do domínio anda de mãos dadas com o módulo de cluster, pois o processo mestre pode bifurcar um novo trabalhador quando um trabalhador encontra um erro. Veja o código abaixo para entender o que quero dizer

Ao usar Domaine a resiliência de separar nosso programa em vários processos de trabalho Cluster, podemos reagir de maneira mais apropriada e lidar com erros com muito mais segurança.

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

Embora Domainesteja pendente de descontinuação e será removido quando a nova substituição ocorrer, conforme declarado na Documentação do Node

Este módulo está com suspensão pendente. Depois que uma API de substituição for finalizada, este módulo será totalmente descontinuado. Os usuários que absolutamente precisam ter a funcionalidade que os domínios fornecem podem depender dela por enquanto, mas devem esperar migrar para uma solução diferente no futuro.

Mas até que a nova substituição não seja introduzida, o Domínio com Cluster é a única boa solução sugerida pela Documentação do Nó.

Para uma compreensão aprofundada Domaine Clusterleitura

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

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

Agradecemos a Stanley Luo por nos compartilhar esta maravilhosa explicação detalhada sobre cluster e domínios

Cluster e domínios

Arejado
fonte
9
Uma palavra de aviso: Domínio está pendente de descontinuação: link . O método sugerido, nos documentos do Node, é usar o cluster: link .
Paul
4
restart your application after every unhandled exception!No caso de usuários de 2000 estarem usando um servidor da web de nó para streaming de vídeo e 1 usuário receber uma exceção, a reinicialização não interromperá todos os outros usuários?
Vikas Bansal
2
@VikasBansal Sim, isso certamente irá interromper todos os usuários e é por isso que é ruim de usar uncaughtExceptione uso Domaincom Clusterem vez de modo, se um usuário enfrenta uma exceção para que apenas seu fio é removido do cluster e criou novo para ele. E você não precisa reiniciar o servidor Node também. Enquanto estiver do outro lado, se você usar, uncaughtExceptionprecisará reiniciar o servidor sempre que algum usuário enfrentar algum problema. Portanto, use Domínio com Cluster.
Arejado,
3
o que devemos fazer quando domainestiver totalmente obsoleto e removido?
Jas
3
Encontrei este tutorial para aqueles que não entendem o conceito de clustere workers: sitepoint.com/…
Stanley Luo
81

Coloquei esse código diretamente nas minhas declarações de solicitação e declarações globais:

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

funciona para mim. a única coisa que eu não gosto é que não recebo tanta informação quanto gostaria se deixasse a coisa falhar.

hvgotcodes
fonte
45
Uma palavra de cautela: esse método funciona bem, mas lembre-se de que TODAS as respostas HTTP precisam ser finalizadas corretamente. Isso significa que, se ocorrer uma exceção não capturada enquanto você estiver manipulando uma solicitação HTTP, ainda deverá chamar end () no objeto http.ServerResponse. No entanto, você implementa isso, é com você. Se você não fizer isso, a solicitação será interrompida até que o navegador desista. Se você tiver um número suficiente dessas solicitações, o servidor poderá ficar sem memória.
BMiner 13/11/11
3
@BMiner, você poderia fornecer uma melhor implementação? Percebi esse problema (solicitação interrompida), então isso realmente não é melhor do que apenas reiniciar o servidor usando foreveralgo assim.
pixelfreak
6
Isso exige uma explicação detalhada. Sei que isso é péssimo, mas sempre que ocorre uma exceção não capturada, seu servidor precisa reiniciar o mais rápido possível. Realmente, o objetivo do evento 'uncaughtException' é usá-lo como uma oportunidade para enviar um email de aviso e, em seguida, usar process.exit (1); desligar o servidor. Você pode usar para sempre ou algo assim para reiniciar o servidor. Qualquer solicitação HTTP pendente atingirá o tempo limite e falhará. Seus usuários ficarão bravos com você. Mas, é a melhor solução. Porque você pergunta? Caixa stackoverflow.com/questions/8114977/...
BMiner
3
Para obter mais informações sobre o erro não capturado, use: console.trace (err.stack);
precisa
2
AVISO: A documentação para o nó diz, em termos inequívocos, que você nunca deve fazer isso, pois é louco perigoso: nodejs.org/api/process.html#process_event_uncaughtexception
Jeremy Logan
28

Conforme mencionado aqui, você encontrará error.stackuma mensagem de erro mais completa, como o número da linha que causou o erro:

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

Experimentar supervisor

npm install supervisor
supervisor app.js

Ou você pode instalar em seu foreverlugar.

Tudo o que isso fará é recuperar o servidor quando ele travar, reiniciando-o.

forever pode ser usado dentro do código para recuperar normalmente qualquer processo que trava.

Os foreverdocumentos têm informações sólidas sobre tratamento de saída / erro programaticamente.

Raynos
fonte
9
Certamente, essa não pode ser a solução ... No período em que o servidor estiver inativo, ele não poderá responder às novas solicitações recebidas. Uma exceção pode ser lançada do código do aplicativo - o servidor precisa responder com um erro 500, não apenas travar e esperar que seja reiniciado.
Ant Kutschera
20
Portanto, como hacker, pode-se descobrir que eles precisam enviar uma solicitação simples ao servidor e perder um parâmetro de solicitação - que leva a um undef no javascript que causa a falha do node.js. Com sua sugestão, eu posso matar todo o seu cluster repetidamente. A resposta é fazer com que o aplicativo falhe normalmente - ou seja, lide com a exceção não capturada e não trava. e se o servidor estivesse lidando com muitas sessões de voip? não é aceitável que ele queime e queime e que todas as sessões existentes morram com ele. seus usuários sairiam em breve.
Ant Kutschera
5
@AntKutschera é por isso que as exceções devem ser casos excepcionais. As exceções devem ser acionadas apenas em situações nas quais você não pode se recuperar e onde o processo precisa travar. Você deve usar outros meios para lidar com esses casos excepcionais . Mas entendo o seu ponto. Você deve falhar normalmente sempre que possível. No entanto, casos em que continuar com um estado corrompido causarão mais danos.
Raynos 17/05
2
Sim, existem diferentes escolas de pensamento aqui. Do jeito que eu aprendi (Java em vez de Javascript), existem expectativas aceitáveis ​​que você deve esperar, conhecidas talvez como exceções de negócios, e há exceções ou erros de tempo de execução, nas quais você não deve se recuperar, como falta de memória. Um problema em não falhar normalmente é que algumas bibliotecas que escrevo podem declarar que lançam uma exceção no caso de algo recuperável, digamos onde um usuário pode corrigir sua entrada. em seu aplicativo, você não ler meus documentos e apenas acidente, onde o usuário poderia ter sido ableto recuperar
Ant Kutschera
1
@AntKutschera É por isso que registramos exceções. Você deve analisar seus logs de produção em busca de exceções comuns e descobrir se e como poderia se recuperar deles, em vez de deixar o servidor travar. Eu usei essa metodologia com PHP, Ruby on Rails e Node. Independentemente de você sair ou não de um processo, toda vez que gera um erro 500, você está fazendo um desserviço aos seus usuários. Esta não é uma prática específica do JavaScript ou do nó.
Eric Elliott
7

O uso do try-catch pode resolver os erros não detectados, mas em algumas situações complexas, ele não funciona corretamente, como capturar a função assíncrona. Lembre-se de que no Node, qualquer chamada de função assíncrona pode conter uma operação potencial de falha de aplicativo.

O uso uncaughtExceptioné uma solução alternativa, mas é reconhecido como ineficiente e provavelmente será removido nas versões futuras do Node, portanto, não conte com isso.

A solução ideal é usar o domínio: http://nodejs.org/api/domain.html

Para garantir que seu aplicativo esteja em funcionamento, mesmo que o servidor tenha travado, use as seguintes etapas:

  1. use o cluster de nós para dividir vários processos por núcleo. Portanto, se um processo morreu, outro processo será inicializado automaticamente. Confira: http://nodejs.org/api/cluster.html

  2. use domain para capturar operação assíncrona em vez de usar try-catch ou uncaught. Não estou dizendo que tentar pegar ou não ser um mau pensamento!

  3. use forever / supervisor para monitorar seus serviços

  4. adicione daemon para executar seu aplicativo de nó: http://upstart.ubuntu.com

espero que isto ajude!

Nam Nguyen
fonte
4

Dê uma chance ao módulo do nó pm2, ele é muito consistente e possui ótima documentação. Gerente de processo de produção para aplicativos Node.js. com um balanceador de carga interno. evite uncaughtException para esse problema. https://github.com/Unitech/pm2

Virendra Rathore
fonte
`reinicie seu aplicativo após todas as exceções não tratadas!` No caso de 2000 usuários usarem um servidor da Web para transmissão de vídeo e 1 usuário receber uma exceção, a reinicialização não interromperá todos os outros usuários?
Vikas Bansal
Fiquei tão feliz quando descobri o PM2. grande parte do software
Mladen Janjetovic
0

UncaughtException é "um mecanismo muito bruto" (tão verdadeiro) e os domínios estão obsoletos agora. No entanto, ainda precisamos de algum mecanismo para detectar erros em domínios (lógicos). A biblioteca:

https://github.com/vacuumlabs/yacol

pode ajudá-lo a fazer isso. Com um pouco de escrita extra, você pode ter boas semânticas de domínio em todo o seu código!

Tomas Kulich
fonte
0

Funciona muito bem em 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
fonte