Como depurar o erro ECONNRESET no Node.js.

288

Estou executando um aplicativo Express.js usando o Socket.io para um aplicativo de bate-papo e recebo o seguinte erro aleatoriamente cerca de 5 vezes durante 24h. O processo do nó é encerrado para sempre e é reiniciado imediatamente.

O problema é que reiniciar o Express expulsa meus usuários de suas salas e ninguém quer isso.

O servidor da web é proxy por HAProxy. Não há problemas de estabilidade de soquete, apenas usando transportes de websockets e flashsockets. Não posso reproduzir isso de propósito.

Este é o erro com o nó v0.10.11:

    events.js:72
            throw er; // Unhandled 'error' event
                  ^
    Error: read ECONNRESET     //alternatively it s a 'write'
        at errnoException (net.js:900:11)
        at TCP.onread (net.js:555:19)
    error: Forever detected script exited with code: 8
    error: Forever restarting script for 2 time

EDIT (22/07/2013)

Adicionados o manipulador de erros do cliente socket.io e o manipulador de exceções não capturado. Parece que este captura o erro:

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

Portanto, suspeito que não seja um problema do Socket.io, mas uma solicitação HTTP para outro servidor que eu faço ou uma conexão MySQL / Redis. O problema é que a pilha de erros não me ajuda a identificar meu problema de código. Aqui está a saída do log:

    Error: read ECONNRESET
        at errnoException (net.js:900:11)
        at TCP.onread (net.js:555:19)

Como sei o que causa isso? Como aproveito ao máximo o erro?

Ok, não muito detalhado, mas aqui está o stacktrace com Longjohn:

    Exception caught: Error ECONNRESET
    { [Error: read ECONNRESET]
      code: 'ECONNRESET',
      errno: 'ECONNRESET',
      syscall: 'read',
      __cached_trace__:
       [ { receiver: [Object],
           fun: [Function: errnoException],
           pos: 22930 },
         { receiver: [Object], fun: [Function: onread], pos: 14545 },
         {},
         { receiver: [Object],
           fun: [Function: fireErrorCallbacks],
           pos: 11672 },
         { receiver: [Object], fun: [Function], pos: 12329 },
         { receiver: [Object], fun: [Function: onread], pos: 14536 } ],
      __previous__:
       { [Error]
         id: 1061835,
         location: 'fireErrorCallbacks (net.js:439)',
         __location__: 'process.nextTick',
         __previous__: null,
         __trace_count__: 1,
         __cached_trace__: [ [Object], [Object], [Object] ] } }

Aqui eu sirvo o arquivo de política do soquete flash:

    net = require("net")
    net.createServer( (socket) =>
      socket.write("<?xml version=\"1.0\"?>\n")
      socket.write("<!DOCTYPE cross-domain-policy SYSTEM \"http://www.macromedia.com/xml/dtds/cross-domain-policy.dtd\">\n")
      socket.write("<cross-domain-policy>\n")
      socket.write("<allow-access-from domain=\"*\" to-ports=\"*\"/>\n")
      socket.write("</cross-domain-policy>\n")
      socket.end()
    ).listen(843)

Essa pode ser a causa?

Samson
fonte
3
@GottZ talvez isso possa ajudar (falei com alguém que trabalha no nó js) gist.github.com/samsonradu/1b0c6feb438f5a53e30e . Vou implantar o manipulador socket.error hoje e avisá-lo.
Samson,
1
@Gottz o socket.error manipula não ajuda, mas process.on ('uncaughtException') captura o erro. Aqui está o console.log do erro: {[Error: read ECONNRESET] code: 'ECONNRESET', errno: 'ECONNRESET', syscall: 'read'}
Samson:
1
ECONNRESET pode ser um problema de rede. Como você sabe, é impossível capturar todas as exceções durante o teste. Alguns aparecerão no seu servidor de produção. Você terá que tornar seu servidor robusto. Você pode lidar com a exclusão da sessão usando o Redis como armazenamento. Isso faz com que suas sessões persistam mesmo após o servidor do nó ficar inativo.
user568109
1
Por que isso está relacionado à exclusão da sessão? Eles são tratados pelos Redis de qualquer maneira.
Samson
3
Você tem pelo menos um soquete TCP escutando que não possui o manipulador definido. Então agora é hora de verificar onde que um é: D
Moss

Respostas:

253

Você já deve ter adivinhado: é um erro de conexão.

"ECONNRESET" significa que o outro lado da conversa do TCP fechou abruptamente o final da conexão. Provavelmente, isso ocorre devido a um ou mais erros de protocolo do aplicativo. Você pode olhar para os logs do servidor da API para ver se há alguma reclamação.

Mas como você também está procurando uma maneira de verificar o erro e potencialmente depurar o problema, consulte " Como depurar um erro de interrupção de soquete no NodeJS? ", Publicado no stackoverflow em relação a uma pergunta semelhante.

Solução rápida e suja para o desenvolvimento :

Use longjohn , você obtém rastreamentos de pilha longos que conterão as operações assíncronas.

Solução limpa e correta : tecnicamente, no nó, sempre que você emitir um 'error'evento e ninguém o ouvir, ele será lançado . Para fazê-lo não jogar, coloque um ouvinte nele e faça você mesmo. Dessa forma, você pode registrar o erro com mais informações.

Para ter um ouvinte para um grupo de chamadas, você pode usar domínios e também capturar outros erros no tempo de execução. Certifique-se de que cada operação assíncrona relacionada ao http (Servidor / Cliente) esteja em um contexto de domínio diferente em comparação com as outras partes do código, se o domínio escutará automaticamente os erroreventos e o propagará para seu próprio manipulador. Portanto, você apenas ouve esse manipulador e obtém os dados do erro. Você também obtém mais informações gratuitamente.

EDIT (22/07/2013)

Como escrevi acima:

"ECONNRESET" significa que o outro lado da conversa do TCP fechou abruptamente o final da conexão. Provavelmente, isso ocorre devido a um ou mais erros de protocolo do aplicativo. Você pode olhar para os logs do servidor da API para ver se há alguma reclamação.

O que também poderia ser o caso: em momentos aleatórios, o outro lado está sobrecarregado e simplesmente mata a conexão como resultado. Se for esse o caso, depende exatamente do que você está se conectando ...

Mas uma coisa é certa: você realmente tem um erro de leitura na sua conexão TCP que causa a exceção. Você pode ver isso observando o código de erro que você postou em sua edição, o que o confirma.

e-sushi
fonte
Não precisa significar "abruptamente fechado". Geralmente, resulta da gravação em uma conexão que o ponto já havia fechado normalmente. Isso fará com que ele emita um RST.
Marquês de Lorne #
1
@EJP Havia uma boa razão pela qual escrevi “abruptamente”. O erro (sem aviso) indica que a conexão foi redefinida por pares. Uma conexão existente foi fechada à força pelo ponto remoto. Um fechamento forçado é abrupto desde inesperado! (Isso normalmente ocorre se o aplicativo de mesmo nível na máquina remota for parado subitamente, a máquina for reiniciada ou se o aplicativo de pares tiver usado um "fechamento rígido" no soquete remoto. Esse erro também poderá resultar se uma conexão for interrompida devido à atividade "manter ativo" detectar uma falha enquanto uma ou mais operações estão em andamento ... estas operações e operações subsequentes irá falhar).
e-sushi
2
Recebo esse erro ao enviar em lote cerca de 100 chamadas de API simultaneamente do navegador (Chrome) para teste. Imagino que o Chrome fique sobrecarregado e acabe com algumas das conexões ... @ Samson - o que há de errado em processar cada solicitação em seu próprio domínio e capturar erros de domínio sem reiniciar o servidor?
supershnee
2
@supershnee Você quase sempre deve reiniciar o servidor após uma exceção não capturada, pois seus dados, aplicativo e o node.js estão em um estado desconhecido. Continuar após uma exceção coloca seus dados em risco. Se você quiser saber mais, consulte os documentos do Node em processo ou os documentos do Node em domínios .
C1moore 31/08/2015
39

Um servidor tcp simples que eu tinha para servir o arquivo de políticas flash estava causando isso. Agora posso pegar o erro usando um manipulador:

# serving the flash policy file
net = require("net")

net.createServer((socket) =>
  //just added
  socket.on("error", (err) =>
    console.log("Caught flash policy server socket error: ")
    console.log(err.stack)
  )

  socket.write("<?xml version=\"1.0\"?>\n")
  socket.write("<!DOCTYPE cross-domain-policy SYSTEM \"http://www.macromedia.com/xml/dtds/cross-domain-policy.dtd\">\n")
  socket.write("<cross-domain-policy>\n")
  socket.write("<allow-access-from domain=\"*\" to-ports=\"*\"/>\n")
  socket.write("</cross-domain-policy>\n")
  socket.end()
).listen(843)
Sansão
fonte
2
Há algo de errado com o código? Devo ter verificado se o soquete é gravável antes de escrever?
Samson
Doh, você não viu que já havia encontrado a solução antes de eu postar praticamente a mesma coisa :) Quanto à sua pergunta, mesmo que você verifique se o soquete é gravável, pode não ser quando você o escreve microssegundos mais tarde e ainda geraria um erro, então esse é "o caminho" para ter certeza.
Joachim Isaksson
ok, e existe uma saída segura se isso? como socket.close () dentro do manipulador de erros? porque eu acho que a minha carga de CPU está aumentando após esses erros (não tenho certeza)
Samson
2
Eu sempre chamei socket.destroy()o manipulador de erros para ter certeza. Infelizmente, não consigo encontrar a documentação necessária, mas ela não emite um erro.
Joachim Isaksson
socket.destroy () salvou meu dia, o que quer que funcione !! Obrigado!
Firas Abd Alrahman
27

Eu tive um problema semelhante em que os aplicativos começaram a apresentar erros após uma atualização do Node. Acredito que isso possa ser rastreado até a versão Nó v0.9.10 deste item:

  • net: não suprima ECONNRESET (Ben Noordhuis)

As versões anteriores não apresentariam erros nas interrupções do cliente. Uma interrupção na conexão do cliente lança o erro ECONNRESET no nó. Acredito que essa funcionalidade seja destinada ao Node, portanto a correção (pelo menos para mim) foi lidar com o erro, o que acredito que você fez em exceções não capturadas. Embora eu lide com isso no manipulador net.socket.

Você pode demonstrar isso:

Crie um servidor de soquete simples e obtenha o Node v0.9.9 e v0.9.10.

require('net')
    .createServer( function(socket) 
    {
           // no nothing
    })
    .listen(21, function()
     {
           console.log('Socket ON')
    })

Inicie usando a v0.9.9 e tente fazer o FTP neste servidor. Estou usando FTP e porta 21 apenas porque estou no Windows e tenho um cliente FTP, mas nenhum cliente telnet é útil.

Então, do lado do cliente, basta interromper a conexão. (Estou apenas fazendo Ctrl-C)

Você deve ver SEM ERRO ao usar o Nó v0.9.9 e ERRO ao usar o Nó v.0.9.10 e superior.

Na produção, eu uso a v.0.10. alguma coisa e ainda dá o erro. Mais uma vez, acho que isso se destina e a solução é lidar com o erro no seu código.

John Williams
fonte
3
Obrigado, eu mesmo consegui! É importante não permitir que os erros se propaguem para uncaughtException, pois isso torna todo o aplicativo instável. Por exemplo, depois de pegar cerca de 10 erros ECONNRESET o servidor, por vezes, tornou-se sem resposta (apenas congelado e didn t lidar com quaisquer conexões)
Samson
Também sabia sobre a alteração da versão do nó que não suprimiu mais o erro, mas vendo tantos problemas aparecendo e sendo resolvidos a cada versão, prefiro optar pela mais recente. Agora estou usando V0.10.13 btw
Samson
16

Teve o mesmo problema hoje. Após alguma pesquisa, encontrei uma --abort-on-uncaught-exceptionopção node.js muito útil . Ele não apenas fornece um rastreamento de pilha de erros muito mais detalhado e útil, mas também salva o arquivo principal na falha do aplicativo, permitindo mais depuração.

Suzana_K
fonte
4
estranho que uma nova resposta a esta pergunta de idade deve aparecer como eu estou olhando - mas isso é ótimo, obrigado
Semicolon
13

Eu estava enfrentando o mesmo problema, mas mitiguei-o colocando:

server.timeout = 0;

antes server.listen . serveré um servidor HTTP aqui. O tempo limite padrão é de 2 minutos, conforme a documentação da API .

Ashish Kaila
fonte
5
Esta não é uma solução, mas uma solução rápida que interrompe as coisas sem gerar um erro.
Nishant Ghodke
9

Outro caso possível (mas raro) pode ser se você tiver comunicações servidor a servidor e tiver definido server.maxConnectionsum valor muito baixo.

Na lib principal do net , net.js , chamará o clientHandle.close()que também causará o erro ECONNRESET:

if (self.maxConnections && self._connections >= self.maxConnections) {
  clientHandle.close(); // causes ECONNRESET on the other end
  return;
}
happy_marmoset
fonte
Ótima ligação, mas maxConnectionso valor padrão é Infinity. Este seria apenas o caso (como você disse) se você tiver substituído explicitamente esse valor.
Gajus
7

Sim, sua veiculação do arquivo de políticas pode definitivamente causar a falha.

Para repetir, basta adicionar um atraso ao seu código:

net.createServer( function(socket) 
{
    for (i=0; i<1000000000; i++) ;
    socket.write("<?xml version=\"1.0\"?>\n");

… E use telnet para conectar à porta. Se você desconectar o telnet antes que o atraso expire, ocorrerá uma falha (exceção não detectada) quando o socket.write gerar um erro.

Para evitar a falha aqui, basta adicionar um manipulador de erros antes de ler / gravar o soquete:

net.createServer(function(socket)
{
    for(i=0; i<1000000000; i++);
    socket.on('error', function() { console.log("error"); });
    socket.write("<?xml version=\"1.0\"?>\n");
}

Ao tentar a desconexão acima, você receberá uma mensagem de log em vez de uma falha.

E quando terminar, lembre-se de remover o atraso.

Joachim Isaksson
fonte
6

Eu também recebo o erro ECONNRESET durante o meu desenvolvimento, da maneira que o resolvo é por não usar o nodemon para iniciar meu servidor, apenas o uso "node server.js"para iniciar meu servidor corrigiu meu problema.

É estranho, mas funcionou para mim, agora nunca mais vejo o erro ECONNRESET.

Andrew Lam
fonte
4

Eu também tinha esse erro e consegui resolvê-lo após dias de depuração e análise:

minha solução

Para mim, o VirtualBox (para Docker) foi o problema. Eu tinha o encaminhamento de porta configurado na minha VM e o erro ocorreu apenas na porta encaminhada.

conclusões gerais

As seguintes observações podem economizar dias de trabalho que tive que investir:

  • Para mim, o problema ocorreu apenas nas conexões de localhost para localhost em uma porta. -> verificar a alteração de qualquer uma dessas constantes resolve o problema.
  • Para mim, o problema só ocorreu na minha máquina -> deixe alguém tentar.
  • Para mim, o problema só ocorreu depois de um tempo e não pôde ser reproduzido de forma confiável
  • Meu problema não pôde ser inspecionado com nenhum dos nós ou ferramentas expressas (depuração). -> não perca tempo com isso

-> descubra se algo está mexendo com sua rede (configurações), como VMs, firewalls etc., essa provavelmente é a causa do problema.

Waog
fonte
2

Resolvi o problema simplesmente conectando-me a uma rede diferente . Esse é um dos possíveis problemas.

Como discutido acima, ECONNRESET significa que a conversa TCP fechou abruptamente seu final da conexão.

Sua conexão à Internet pode estar impedindo a conexão com alguns servidores. No meu caso, eu estava tentando conectar-se ao mLab (serviço de banco de dados em nuvem que hospeda bancos de dados MongoDB). E meu ISP está bloqueando isso.

Yousef
fonte
Este funcionou para mim, meu código, que estava funcionando bem poucas horas atrás, de repente parou de funcionar, a mudança de rede causou o problema
Aklank Jain
2

Eu resolvi esse problema:

  • Desligando minha conexão wifi / ethernet e ligando.
  • Eu digitei: npm updateno terminal para atualizar o npm.
  • Tentei sair da sessão e entrar novamente

Depois disso, tentei o mesmo comando npm e o bom foi que funcionou. Eu não tinha certeza se é assim tão simples.

Estou usando o CENTOS 7

muhammad tayyab
fonte
0

Eu tive o mesmo problema e parece que a versão do Node.js foi o problema.

Instalei a versão anterior do Node.js (10.14.2) e tudo estava bem usando o nvm (permite instalar várias versões do Node.js e alternar rapidamente de uma versão para outra).

Não é uma solução "limpa", mas pode atendê-lo temporariamente.

Sylvain
fonte
0

Acabei de descobrir isso, pelo menos no meu caso de uso.

Eu estava recebendo ECONNRESET. Aconteceu que, da maneira como meu cliente foi configurado, ele estava atingindo o servidor com uma chamada de API várias vezes muito rapidamente - e só precisava atingir o ponto de extremidade uma vez.

Quando consertei isso, o erro se foi.

VikR
fonte
-2

Tente adicionar essas opções ao socket.io:

const options = { transports: ['websocket'], pingTimeout: 3000, pingInterval: 5000 };

Eu espero que isso te ajude !

sol404
fonte