2012-05-09 20 views
9

Tenía una aplicación Rails 3 en Nginx/Passenger que acabo de mover a Nginx/Thin (1.3.1). Sin embargo, mi aplicación ahora es claramente más lenta que en Passenger. Una gran cantidad de solicitudes de tiempo de espera también.Thin server underperforming/¿Cómo funcionan los servidores web con evented?

Thin es un servidor web con evented. Por lo que he leído sobre los servidores web con evented, no tienen un concepto de trabajadores. Un "trabajador" maneja todo. Entonces, si una solicitud está esperando en IO, thin simplemente pasa a la siguiente solicitud y luego a una. Una explicación que leí sobre los servidores con evented decía que los servidores con evented deben funcionar tan bien o mejor que los servidores basados ​​en el trabajador, ya que solo están vinculados por los recursos del sistema.

Sin embargo, el uso de mi CPU es muy poco. El uso de mi memoria también es muy pequeño, y tampoco ocurre mucho IO. Mi aplicación solo hace algunas consultas MySQL.

¿Cuál es el cuello de botella aquí? ¿No debería mi servidor delgado manejar las solicitudes hasta que la CPU esté al 100%? ¿Tengo que hacer algo diferente en mi aplicación para que tenga un mejor rendimiento con un servidor con un excelente servicio?

Respuesta

13

Sergio es correcto. Su aplicación, en este punto, probablemente sea mejor en el modelo tradicional de Apache/Pasajero. Si toma la ruta prevista, especialmente en plataformas de un solo subproceso como Ruby, NUNCA puede bloquear nada, ya sea la base de datos, los servidores de caché, otras solicitudes HTTP que pueda hacer, nada.

Esto es lo que hace que la programación asincrónica (evented) sea más difícil: es fácil bloquear cosas, generalmente en forma de E/S de disco síncronas o resoluciones de DNS. Los frameworks sin bloqueo (evented) tales como nodejs son cuidadosos ya que (casi) nunca le proporcionan una llamada de función de marco que bloquea, sino que todo se maneja mediante devoluciones de llamada (incluidas las consultas DB).

Esto podría ser más fácil de visualizar si nos fijamos en el corazón de un servidor de un solo subproceso no bloqueante:

while(wait_on_sockets(/* list<socket> */ &$sockets, /* event */ &$what, $timeout)) { 
    foreach($socketsThatHaveActivity as $fd in $sockets) { 
     if($what == READ) { // There is data availabe to read from this socket 
      $data = readFromSocket($fd); 
      processDataQuicklyWithoutBlocking($data); 
     } 
     elseif ($what == WRITE && $data = dataToWrite($fd)) { // This socket is ready to be written to (if we have any data) 
      writeToSocket($fd, $data);  
     } 
    } 
} 

Lo que ves arriba se llama el bucle de eventos. wait_on_sockets suele ser proporcionado por el SO en forma de una llamada al sistema, como select, poll, epoll o kqueue. Si processDataQuicklyWithoutBlocking tarda demasiado, el búfer de red de la aplicación mantenido por el sistema operativo (solicitudes nuevas, datos entrantes, etc.) se llenará y hará que rechace conexiones nuevas y agote las existentes, ya que $ socketsThatHaveActivity no se está manejando lo suficientemente rápido . Esto es diferente de un servidor enhebrado (por ejemplo, una instalación típica de Apache) porque cada conexión se sirve utilizando un proceso/subproceso independiente, de modo que los datos entrantes se leerán en la aplicación tan pronto como lleguen, y los datos salientes se enviarán sin demora. .

Lo que las plataformas sin bloqueo como nodejs hacen cuando realiza (por ejemplo) una consulta de DB es agregar la conexión de socket del servidor de bases de datos a la lista de sockets que se monitorean ($ sockets), incluso si su consulta un tiempo, su (único) hilo no está bloqueado en ese socket. Sino que sirven de devolución de llamada:

$db.query("...sql...", function($result) { ..handle result ..}); 

Como se puede ver arriba, db.query vuelve inmediatamente sin ningún tipo de bloqueo en el servidor db en absoluto.Esto también significa que con frecuencia tiene que escribir código como este, a menos que el lenguaje de programación en sí es compatible con funciones asíncronas (como el nuevo C#):

$db.query("...sql...", function($result) { $httpResponse.write($result); $connection.close(); }); 

La regla de nunca jamás-bloque puede ser un poco relajado si tiene muchos procesos que cada uno ejecuta un bucle de eventos (generalmente la forma de ejecutar un clúster de nodos), o usa un grupo de subprocesos para mantener el bucle de eventos (embarcadero de java, netty, etc., puede escribir el suyo en C/C++). Mientras que un hilo está bloqueado en algo, otros hilos aún pueden hacer el ciclo de evento. Pero bajo una carga lo suficientemente pesada, incluso estas no funcionarían. Así que NUNCA NUNCA BLOQUEAR en un servidor con el ventilador puesto.

Como puede ver, los servidores evented generalmente intentan resolver un problema diferente: pueden tener una gran cantidad de conexiones abiertas. Donde sobresalen es simplemente empujando bytes con cálculos ligeros (por ejemplo, servidores de cometas, memorias caché como memcached, barniz, proxies como nginx, squid, etc.). No vale la pena que, aunque escalan mejor, los tiempos de respuesta generalmente tienden a aumentar (nada es mejor que reservar un hilo completo para una conexión). Por supuesto, puede que no sea económica/computacionalmente factible ejecutar la misma cantidad de subprocesos como # de conexiones simultáneas.

Ahora volviendo a su problema, recomiendo que todavía tenga Nginx, ya que es excelente en la administración de la conexión (que está basada en eventos), generalmente significa manejar HTTP keep-alives, SSL, etc. Debe conectar esto a su aplicación de Rails usando FastCGI, donde todavía necesita ejecutar trabajadores, pero no tiene que volver a escribir su aplicación para que se destaque por completo. También debe dejar que Nginx sirva contenido estático; no tiene sentido que sus trabajadores de Rails estén atados con algo que normalmente Nginx puede hacer mejor. Este enfoque generalmente se escala mucho mejor que Apache/Passenger, especialmente si ejecuta un sitio web de alto tráfico.

Si puede escribir toda su aplicación para ser descubierta, entonces genial, pero no tengo idea de cuán fácil o difícil es eso en Ruby.

+0

Guau .. gracias por la respuesta detallada Tejas. Entonces, los puntos de referencia que leo de la red ... ¿son para un género de aplicación completamente diferente? El propio sitio de Thin ofrece una aplicación de rieles como aplicación de ejemplo para thin. http://code.macournoyer.com/thin/. Tenía la impresión de que podría reemplazar al pasajero con delgado y que todo será hortera. –

+0

Siempre y cuando no bloquee ningún lugar, debería poder recrear esos puntos de referencia. – tejas

3

Sí, Thin hace evented I/O, pero solo para la parte HTTP. Lo que significa que puede recibir datos HTTP entrantes mientras procesa una solicitud. Sin embargo, todas las E/S de bloqueo que haces durante el procesamiento siguen bloqueando. Si su MySQL tarda en responder, la cola de solicitudes delgadas se llenará.

Para "más" servidor web evented, debe consultar Rainbows.

+0

Hola Sergio. Disculpe mi comprensión peatonal de estos conceptos. Leí que la gema mysql2 para rieles también tiene E/S. Además, si ese es el caso, ¿no sería mejor un servidor basado en el trabajador? Solo corro como 2 instancias de thin frente a las 25 instancias de pasajeros que corría antes. –

+0

@CoffeeBite: *** puede hacer llamadas asincrónicas, sí, pero no es automático y tiene que escribir el código para que suceda. Por defecto, es sincrónico. –

+0

@CoffeeBite: "los trabajadores no siempre serían mejores" - no estoy seguro. Yo mismo uso [Unicornio] (http://unicorn.bogomips.org/) detrás de un nginx. Nginx maneja las E/S HTTP y los unicornios sirven rápidamente las solicitudes. –

Cuestiones relacionadas