2012-10-12 79 views
6

Node.js + expreso Gotas aleatoriamente solicitudes, lo que resulta en una edición de Pasarela interrumpida

Después de mucho Putzing alrededor, finalmente encontré algo que parece que podría ser una pista sólida:

La biblioteca expreso no acepta la solicitud entrante cuando está utilizando el módulo Node + OAuth para realizar varias solicitudes de salida (por ejemplo, a Facebook, Twitter, etc.). Pude determinar esto colocando muchos registros en mi código, donde descubrí que el registro de "solicitud de inicio" (que se describe a continuación) no se activó cuando estaba en medio de una solicitud de salida.

He podido demostrar demostrablemente que cuando el módulo Node + OAuth realiza algunas solicitudes de salida, las solicitudes entrantes a mi API (a través de una ventana del navegador) se bloquearán y no se recibirán hasta una de esas solicitudes OAuth salientes ha terminado.

Por supuesto, ya he hecho:

require('http').globalAgent.maxSockets = 999; 

por una sugerencia en el IRC, he añadido

console.log(require('http').globalAgent.requests); 

Pero esto siempre parece ser === {}, lo que implica que no hay solicitudes entrantes pendientes AFAIK.

Así me quedo a la conclusión de que o bien Node.js o expresar es la elección de a, por alguna razón, las peticiones de entrada del bloque debido a las peticiones salientes, a pesar de que debe haber un montón de sockets disponibles ...

¿Alguien tiene alguna pista sobre cómo resolver esto?


que tienen una API creada en Node.js utilizando Express, Mangosta, etc. desplegado en la nube de Amazon, que funciona de maravilla y rápido 99% del tiempo.

Excepto, de vez en cuando, una solicitud parece de alguna manera se descarta o se ignora. Estoy hablando de solicitudes que generalmente se completan en milisegundos y que al azar no responden sin una imagen clara por qué.

El síntoma es un simple "Tiempo de espera de puerta de enlace" cuando se conecta al punto final de la API. Una solicitud idéntica, hecha desde el mismo cliente con los mismos parámetros, momentos antes o momentos después, funcionará bien.

Por supuesto, mi primer pensamiento fue "duh, server overload!" Así que dediqué mucho tiempo a optimizar mis solicitudes, monogoDB, etc. Finalmente, llegué al punto de que el uso de la CPU/disco/RAM en general (tanto en servidores Node.js como en servidores Mongo) era muy bajo. Uso Scout y RightScale para rastrear mis servidores en tiempo real, y registro cualquier solicitud o consulta que tome más de 100 ms. Mis servidores de nodos actualmente tienen 5GB de RAM GRATIS, 70% de CPU gratis (en el 1er núcleo), etc. Por lo tanto, estoy 99.99% seguro de que no es un problema de rendimiento.

Finalmente, recurrí a un intento desesperado: adjunté un número al azar a todas las solicitudes hechas por mi cliente (s). Luego, en la aplicación node.js, hago un console.log() cuando la solicitud se recibe por primera vez y cuando se completa. Por ejemplo, aquí está el middleware que utilizo en expreso:

var configureAPI = function() { 
    return function(req, res, next) { 
     if(req.body.ruid) 
      console.log(req.body.ruid); 

     // more middleware stuff... 
    }; 
} 
server.configure(function(){ 

    server.use(express.bodyParser()); 
    server.use(configureAPI()); 
    server.use(onError); 

    // ... more config stuff 
} 

Lo que encontré me sorprendió: al parecer, el nodo.La aplicación js ni siquiera está recibiendo las solicitudes en la pregunta. Tengo una aplicación web de Javascript e imprimo el "ruid" enviado con la solicitud a la consola. Siempre que la solicitud tenga éxito, se imprimirá una "ruid" correspondiente en la consola node.js. Cuando se agota el tiempo, no hay.


Editar: más información & depuración.

Mis servidores de aplicaciones realmente comenzaron (y continúan) también a servir PHP (por lo tanto, tienen instalado Apache, etc.). Necesitaba http://streamified.me para servir mi sitio web (PHP) y http://api.streamified.me para servir mi API (node.js) ... así que tengo una línea en mi archivo httpd.conf para causar solicitudes a api.streamified.me (en lugar de streamified.me) para ir a Node.js a través del puerto 8888:

RewriteCond %{HTTP_HOST} ^api.streamified.me 
RewriteRule ^(.*) http://localhost:8888$1 [P] 

por lo tanto, en el mismo archivo httpd.conf, encendí RewriteLogLevel 5 y luego creé un simple PHP + guión CURL en mi localhost a golpear a mi api. streamified.me con una URL aleatoria (que debe provocar que node.js desencadene una respuesta simple "no encontrada") hasta que se produzca un tiempo de espera de puerta de enlace. Aquí puede ver que ha sucedido, y el registro de reescritura muestra que la solicitud fue definitivamente recibida por el servidor de la aplicación y reenviada al puerto 8888 ... pero nunca la recibió node.js (o, al menos, la primera línea de código en la primera línea de middleware que nunca llega ...)

enter image description here


he sido una y otra vez mi código Node.js y estoy bastante seguro de que no tengo bloqueo código, e incluso si lo hiciera, no puedo imaginarlo bloqueando el hilo el tiempo suficiente para perder una solicitud sin que levante una bandera roja en alguna parte.

¿Qué me estoy perdiendo? ¿Hay alguna razón por la cual el socket entrante sería bloqueado? Hago una cantidad justa de solicitudes HTTP a API externas a través de mi aplicación node.js, pero AFAIK no debería estar bloqueando los sockets entrantes.


Por supuesto, tengo un error al iniciar sesión. He permitido que en el nivel de proceso ...

process.addListener("uncaughtException", function (err) { 
    // some logging code 
} 

y en el nivel Express (el manejador onError arriba). Sé que mis funciones de registro de errores funcionan, porque las he visto disparar antes. Pero ninguno de ellos reportan nada en la época de las solicitudes se redujo, ni veo nada en la consola ...


  • Express Versión: 3.0.0rc5
  • Node.js Versión: 0.8. 12
  • 2 instancias de la aplicación Node.js que se ejecuta en una configuración estándar Amazon Cloud (instancias m1.large), detrás de 2 equilibradores de carga, que se conectan a un conjunto 3x réplica del MongoDBs (también m1.large)
+0

¿Ha confirmado que los equilibradores de carga están recibiendo la solicitud y enviándola a los servidores de su nodo con éxito? ¿Con qué frecuencia hace las solicitudes cuando falla? – Bill

+0

Los mismos servidores LB/app también sirven archivos PHP, que nunca resultan en tiempo de espera. Sin embargo, no estoy seguro de cómo confirmar que los LB se están reenviando a los servidores de Nodo, aparte de eso. No estoy mostrando ningún aumento en el tráfico; los registros de apache en la escala de derechos informan de una constante ~ 10 req/seg. –

+0

Encontré un par de errores enumerados que describían problemas similares pero todos fueron corregidos por 0.6.6. Puede intentar actualizar a la última versión ya que ha habido un montón de correcciones/mejoras desde 0.6. También le sugiero que configure un sniffer de red en los servidores de su aplicación para asegurarse de que los servidores realmente están recibiendo los paquetes. – Bill

Respuesta

1

Parece que estás encerrando su hilo Node durante demasiado tiempo, causando que las conexiones entrantes expiren antes de que llegue el momento de manejarlas. El nodo tiene un solo hilo por lo que solo hace una cosa a la vez, no puede elegir bloquear una solicitud entrante debido a una solicitud saliente. Solo puede dejar de aceptar una solicitud entrante porque está ocupado haciendo otra cosa. Necesitas averiguar qué está ocupado haciendo.

Si no realiza las solicitudes de salida, todo funciona bien?Si es así, debe ver el código que realiza esas solicitudes para asegurarse de que no está esperando las respuestas.

+0

Eso tiene sentido. Estoy seguro de que no estoy haciendo ninguna tarea de "sincronización", y utilizo Q para implementar las promesas. Lo único que viene a la mente son los comandos JSON.parse(), que están evaluando cadenas de datos grandes (~ 2MB). ¿Podría este tipo de operaciones bloquear el hilo? –

+0

¿Podrían algunos de los datos devolver mucho más de 2 MB? 2 MB no debe colgar el hilo el tiempo suficiente para soltar las solicitudes (aunque bloqueará las cosas un poco), pero si de vez en cuando intenta analizar cadenas mucho más grandes, podría ser el culpable. Podría intentar reemplazar el análisis sintáctico con una llamada para devolver datos estáticos para ver si eso resuelve el problema. – Bill

+0

Hm, realmente no puedo usar datos estáticos, debido al hecho de que el análisis JSON está afectando posteriormente a las cosas, y al usar datos estáticos, solo estaría probando 1 escenario ... en cualquier caso, comencé a dar salida a análisis() veces, que a veces son alrededor de ~ 100 ms (y a menudo hay varios consecutivos, aunque separados por promesas). Además, acabo de tener la primera ocurrencia de un error OOM "ERROR FATAL: CALL_AND_RETRY_2 Asignación fallida - proceso sin memoria" ... lo que me hace preguntarme si eso podría ser parte del problema ... aunque es la primera vez que He visto tal error ... –

Cuestiones relacionadas