¿Cómo depuro el error ECONNRESET en Node.js?

288

Estoy ejecutando una aplicación Express.js usando Socket.io para una aplicación web de chat y obtengo el siguiente error al azar alrededor de 5 veces durante 24 h. El proceso de nodo se envuelve para siempre y se reinicia de inmediato.

El problema es que reiniciar Express expulsa a mis usuarios de sus habitaciones y nadie quiere eso.

El servidor web está representado por HAProxy. No hay problemas de estabilidad de socket, solo se utilizan websockets y transportes flashsockets. No puedo reproducir esto a propósito.

Este es el error con Node 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

EDITAR (2013-07-22)

Se agregaron tanto el controlador de errores del cliente socket.io como el controlador de excepciones no capturados. Parece que este detecta el error:

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

Entonces sospecho que no es un problema de Socket.io sino una solicitud HTTP a otro servidor que hago o una conexión MySQL / Redis. El problema es que la pila de errores no me ayuda a identificar mi problema de código. Aquí está la salida del registro:

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

¿Cómo sé qué causa esto? ¿Cómo obtengo más del error?

Ok, no muy detallado, pero aquí está el stacktrace con 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] ] } }

Aquí sirvo el archivo de política de socket 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)

¿Puede ser esta la causa?

Samson
fuente
3
@GottZ quizás esto pueda ayudar (habló con alguien que trabaja dentro del nodo js) gist.github.com/samsonradu/1b0c6feb438f5a53e30e . Implementaré el controlador socket.error hoy y te lo haré saber.
Samson
1
@Gottz que maneja socket.error no ayuda, pero process.on ('uncaughtException') detecta el error. Aquí está el console.log del error: {[Error: leer ECONNRESET] código: 'ECONNRESET', errno: 'ECONNRESET', syscall: 'read'}
Samson
1
ECONNRESET podría provenir de un problema de red. Como saben, es imposible detectar todas las excepciones al realizar pruebas. Algunos aparecerán en su servidor de producción. Tendrá que hacer que su servidor sea robusto. Puede manejar la eliminación de la sesión utilizando Redis como almacenamiento. Hace que sus sesiones persistan incluso después de que su servidor de nodo se caiga.
user568109
1
¿Por qué está relacionado con la eliminación de la sesión? Son manejados por Redis de todos modos.
Samson
3
Tiene al menos una escucha de socket TCP que no tiene configurado el controlador. Así que ahora es el momento de verificar dónde está: D
Moss

Respuestas:

253

Es posible que ya lo hayas adivinado: es un error de conexión.

"ECONNRESET" significa que el otro lado de la conversación TCP cerró abruptamente su final de la conexión. Esto probablemente se deba a uno o más errores de protocolo de aplicación. Puede mirar los registros del servidor API para ver si se queja de algo.

Pero dado que también está buscando una forma de verificar el error y potencialmente depurar el problema, debería echar un vistazo a " ¿Cómo depurar un error de bloqueo de socket en NodeJS? ", Que se publicó en stackoverflow en relación con una pregunta similar.

Solución rápida y sucia para el desarrollo :

Use longjohn , obtendrá rastros largos de la pila que contendrán las operaciones asíncronas.

Solución limpia y correcta : Técnicamente, en el nodo, cada vez que emites un 'error'evento y nadie lo escucha, se lanzará . Para que no se lance, póngale un oyente y maneje usted mismo. De esa manera puede registrar el error con más información.

Para tener un oyente para un grupo de llamadas, puede usar dominios y también detectar otros errores en tiempo de ejecución. Asegúrese de que cada operación asíncrona relacionada con http (Servidor / Cliente) esté en un contexto de dominio diferente en comparación con las otras partes del código, el dominio escuchará automáticamente los erroreventos y lo propagará a su propio controlador. Entonces solo escuchas ese controlador y obtienes los datos de error. También obtienes más información gratis.

EDITAR (2013-07-22)

Como escribí arriba:

"ECONNRESET" significa que el otro lado de la conversación TCP cerró abruptamente su final de la conexión. Esto probablemente se deba a uno o más errores de protocolo de aplicación. Puede mirar los registros del servidor API para ver si se queja de algo.

Lo que también podría ser el caso: en momentos aleatorios, el otro lado está sobrecargado y simplemente mata la conexión como resultado. Si ese es el caso, depende de a qué se esté conectando exactamente ...

Pero una cosa es segura: de hecho, tiene un error de lectura en su conexión TCP que causa la excepción. Puedes ver eso mirando el código de error que publicaste en tu edición, que lo confirma.

e-sushi
fuente
No tiene que significar "abruptamente cerrado". Suele ser el resultado de escribir en una conexión que el compañero ya había cerrado normalmente. Eso hará que emita un RST.
Marqués de Lorne
1
@EJP Hubo una buena razón por la que escribí "abruptamente". El error (sin advertencia) indica que la conexión fue restablecida por un par. Una conexión existente fue cerrada por la fuerza por el par remoto. ¡Un cierre forzado es abrupto ya que inesperado! (Esto normalmente se produce si la aplicación de pares en la máquina remota se detiene repentinamente, la máquina se reinicia o la aplicación de pares usa un "cierre duro" en el zócalo remoto. Este error también puede producirse si una conexión se interrumpió debido a la actividad "mantener viva" detectar una falla mientras una o más operaciones están en progreso ... estas operaciones y las operaciones posteriores fallarán.)
e-sushi
2
Recibo este error cuando envío por lotes alrededor de 100 llamadas API casi al mismo tiempo desde el navegador (Chrome) para realizar pruebas. Me imagino que Chrome debe sobrecargarse y eliminar algunas de las conexiones ... @Samson: ¿qué tiene de malo procesar cada solicitud en su propio dominio y detectar errores de dominio sin reiniciar el servidor?
supershnee
2
@supershnee Casi siempre debería reiniciar su servidor después de una excepción no detectada ya que sus datos, aplicación y node.js están en un estado desconocido. Continuar después de una excepción pone sus datos en riesgo. Si desea obtener más información, consulte los documentos de Node en proceso o los documentos de Node en dominios .
c1moore
39

Un servidor tcp simple que tenía para servir el archivo de política flash estaba causando esto. Ahora puedo detectar el error usando un controlador:

# 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ón
fuente
2
¿Hay algo malo con el código? ¿Debería haber comprobado si el socket es grabable antes de escribir?
Samson
Doh, no vi que ya encontraste la solución antes de que publicara más o menos lo mismo :) Sin embargo, en cuanto a tu pregunta, incluso si verificas que el socket es grabable, puede que no sea así cuando escribes microsegundos más tarde y aún arrojaría un error, así que este es "el camino" para estar seguro.
Joachim Isaksson
ok, y hay una salida segura si esto? como socket.close () dentro del controlador de errores? porque creo que mi carga de la CPU está aumentando después de estos errores (no estoy seguro)
Samson
2
Siempre he llamado socket.destroy()al controlador de errores para asegurarme. Lamentablemente, no puedo encontrar documentación si es necesario, pero no emite un error al hacerlo.
Joachim Isaksson
socket.destroy () me salvó el día, lo que sea que funcione !! ¡Gracias!
Firas Abd Alrahman
27

Tuve un problema similar en el que las aplicaciones comenzaron a fallar después de una actualización de Node. Creo que esto se remonta a la versión de nodo v0.9.10 de este artículo:

  • net: no suprimir ECONNRESET (Ben Noordhuis)

Las versiones anteriores no generarían errores en las interrupciones del cliente. Una interrupción en la conexión del cliente arroja el error ECONNRESET en el nodo. Creo que esta es la funcionalidad prevista para Node, por lo que la solución (al menos para mí) fue manejar el error, lo que creo que hizo en excepciones no detectadas. Aunque lo manejo en el controlador net.socket.

Puedes demostrar esto:

Cree un servidor de socket simple y obtenga Node v0.9.9 y v0.9.10.

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

Inícielo usando v0.9.9 y luego intente FTP a este servidor. Estoy usando FTP y el puerto 21 solo porque estoy en Windows y tengo un cliente FTP, pero no tengo un cliente Telnet a mano.

Luego, desde el lado del cliente, simplemente rompa la conexión. (Solo estoy haciendo Ctrl-C)

Debería ver NO ERROR cuando usa Node v0.9.9, y ERROR cuando usa Node v.0.9.10 y superior.

En producción, uso v.0.10. algo y todavía da el error. Nuevamente, creo que esto está destinado y la solución es manejar el error en su código.

John Williams
fuente
3
Gracias, lo clavé yo mismo! Es importante no permitir que los errores se propaguen a uncaughtException porque hace que toda la aplicación sea inestable. Por ejemplo, después de detectar alrededor de 10 errores ECONNRESET, el servidor a veces dejó de responder (simplemente se congeló y no manejó ninguna conexión)
Samson
También sabía sobre el cambio de versión del nodo que ya no suprimía el error, pero al ver tantos problemas apareciendo y resolviéndose cada versión, preferiría la última. Estoy usando V0.10.13 ahora por cierto
Samson
16

Tuve el mismo problema hoy. Después de algunas investigaciones he encontrado una muy útil --abort-on-uncaught-exceptionopción de Node.js . No solo proporciona un seguimiento de la pila de errores mucho más detallado y útil, sino que también guarda el archivo principal en caso de bloqueo de la aplicación, lo que permite una mayor depuración.

Suzana_K
fuente
44
raro que aparezca una nueva respuesta a esta vieja pregunta mientras estoy mirando, pero esto es genial, gracias
Semicolon
13

Estaba enfrentando el mismo problema pero lo mitigé colocando:

server.timeout = 0;

antes server.listen. serveres un servidor HTTP aquí. El tiempo de espera predeterminado es de 2 minutos según la documentación de la API .

Ashish Kaila
fuente
55
Esta no es una solución, sino una solución rápida que romperá las cosas sin arrojar un error.
Nishant Ghodke
9

Otro caso posible (pero raro) podría ser si tiene comunicaciones de servidor a servidor y ha establecido server.maxConnectionsun valor muy bajo.

En el nodo del núcleo lib net.js llamará, clientHandle.close()lo que también causará el error ECONNRESET:

if (self.maxConnections && self._connections >= self.maxConnections) {
  clientHandle.close(); // causes ECONNRESET on the other end
  return;
}
happy_marmoset
fuente
Gran llamada, pero maxConnectionsel valor predeterminado es Infinity. Este sería solo el caso (como usted dijo) si ha anulado explícitamente ese valor.
Gajus
7

Sí, su publicación del archivo de políticas definitivamente puede causar el bloqueo.

Para repetir, solo agregue un retraso a su código:

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

... y usar telnetpara conectarse al puerto. Si desconecta telnet antes de que la demora haya expirado, obtendrá un bloqueo (excepción no detectada) cuando socket.write arroje un error.

Para evitar el bloqueo aquí, solo agregue un controlador de errores antes de leer / escribir el socket:

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

Cuando intente la desconexión anterior, solo recibirá un mensaje de registro en lugar de un bloqueo.

Y cuando haya terminado, recuerde eliminar el retraso.

Joachim Isaksson
fuente
6

También recibo un error ECONNRESET durante mi desarrollo, la forma en que lo soluciono es al no usar nodemon para iniciar mi servidor, solo usar "node server.js"para iniciar mi servidor solucionó mi problema.

Es extraño, pero funcionó para mí, ahora nunca veo el error ECONNRESET nuevamente.

Andrew Lam
fuente
4

También tuve este error y pude resolverlo después de días de depuración y análisis:

mi solución

Para mí VirtualBox (para Docker) fue el problema. Tenía el reenvío de puertos configurado en mi VM y el error solo ocurrió en el puerto reenviado.

conclusiones generales

Las siguientes observaciones pueden ahorrarle días de trabajo que tuve que invertir:

  • Para mí, el problema solo ocurrió en las conexiones de localhost a localhost en un puerto. -> comprobar cambiar cualquiera de estas constantes resuelve el problema.
  • Para mí, el problema solo ocurrió en mi máquina -> dejar que alguien más lo pruebe.
  • Para mí, el problema solo ocurrió después de un tiempo y no se pudo reproducir de manera confiable
  • Mi problema no se pudo inspeccionar con ninguna de las herramientas de nodos o expresiones (depuración). -> no pierdas el tiempo en esto

-> descubra si algo está jugando con su red (configuraciones), como máquinas virtuales, firewalls, etc., esta es probablemente la causa del problema.

Waog
fuente
2

Resolví el problema simplemente conectándome a una red diferente . Ese es uno de los posibles problemas.

Como se discutió anteriormente, ECONNRESET significa que la conversación TCP cerró abruptamente su final de la conexión.

Es posible que su conexión a Internet le impida conectarse a algunos servidores. En mi caso, intentaba conectarme a mLab (servicio de base de datos en la nube que aloja las bases de datos MongoDB). Y mi ISP lo está bloqueando.

Yousef
fuente
Este funcionó para mí, mi código, que funcionaba bien unas horas atrás, de repente dejó de funcionar, el cambio de red causó el problema
Aklank Jain
2

Había resuelto este problema:

  • Apagar mi conexión wifi / ethernet y encender.
  • npm updateEscribí : en la terminal para actualizar npm.
  • Intenté cerrar sesión en la sesión e iniciar sesión nuevamente

Después de eso probé el mismo comando npm y lo bueno fue que funcionó. No estaba seguro de que sea así de simple.

Estoy usando CENTOS 7

muhammad tayyab
fuente
0

Tuve el mismo problema y parece que la versión Node.js fue el problema.

Instalé la versión anterior de Node.js (10.14.2) y todo estaba bien usando nvm (le permite instalar varias versiones de Node.js y cambiar rápidamente de una versión a otra).

No es una solución "limpia", pero puede servirle temporalmente.

Sylvain
fuente
0

Acabo de descubrir esto, al menos en mi caso de uso.

Me estaba poniendo ECONNRESET. Resultó que la forma en que estaba configurado mi cliente, estaba golpeando al servidor con una llamada API una tonelada de veces realmente rápido, y solo necesitaba llegar al punto final una vez.

Cuando arreglé eso, el error desapareció.

VikR
fuente
-2

Intente agregar estas opciones a socket.io:

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

Espero que esto ayude !

sol404
fuente