ruby rack thin

rack ruby



Simultaneidad de rack: rack.multithread, async.callback o ambos? (1)

Nota: uso Thin como sinónimo para todos los servidores web que implementan la extensión async Rack (es decir, ¡Rainbows !, Ebb, versiones futuras de Puma, ...)

Q1. Correcto. Envolverá la generación de respuesta (también conocida como call ) en EventMachine.defer { ... } , lo que hará que EventMachine lo inserte en su grupo de subprocesos integrado.

Q2. Usar async.callback junto con EM.defer realidad no tiene demasiado sentido, ya que básicamente también usaría el grupo de hilos, terminando con una construcción similar a la descrita en Q1. Usar async.callback tiene sentido cuando solo se usan las librerías eventmachine para IO. Thin enviará la respuesta al cliente una vez que se env[''async.callback''] con una respuesta de Rack normal como argumento.

Si el cuerpo es un EM::Deferrable , Thin no cerrará la conexión hasta que el aplazamiento tenga éxito. Un secreto bastante bien guardado: si desea algo más que un largo sondeo (es decir, mantenga la conexión abierta después de enviar una respuesta parcial), también puede devolver un EM::Deferrable como objeto cuerpo directamente sin tener que usar throw :async o un estado código de -1 .

Q3. Estás adivinando correctamente. La publicación con subprocesos puede mejorar la carga en una aplicación Rack que de lo contrario no se modificará. Veo un 20% de mejora para las aplicaciones simples de Sinatra en mi máquina con Ruby 1.9.3, incluso más cuando se ejecuta en Rubinius o JRuby, donde se pueden utilizar todos los núcleos. El segundo enfoque es útil si escribe su aplicación de manera eficaz.

Puedes lanzar un montón de magia y hacks sobre Rack para que una aplicación no codificada haga uso de esos mecanismos (ver em-synchrony o sinatra-synchrony), pero eso te dejará en la depuración y en el infierno de la dependencia.

El enfoque asincrónico tiene mucho sentido con las aplicaciones que tienden a resolverse mejor con un enfoque innovador, como un chat web . Sin embargo, no recomendaría utilizar el método de subprocesamiento para implementar el sondeo largo, ya que cada conexión de sondeo bloqueará un hilo. Esto te dejará con una tonelada de hilos o conexiones con las que no podrás lidiar. El grupo de subprocesos de EM tiene un tamaño de 20 subprocesos por defecto, lo que lo limita a 20 conexiones en espera por proceso.

Podría usar un servidor que cree un nuevo hilo para cada conexión entrante, pero crear hilos es costoso (excepto en MacRuby, pero no usaría MacRuby en ninguna aplicación de producción). Algunos ejemplos son serv y net-http-server . Idealmente, lo que desea es un mapeo n: m de solicitudes e hilos. Pero no hay ningún servidor ofreciendo eso.

Si desea obtener más información sobre el tema: hice una presentación sobre esto en Rocky Mountain Ruby (y un montón de otras conferencias). Una grabación de video se puede encontrar en los confreaks .

Estoy intentando comprender completamente las opciones para el manejo concurrente de solicitudes en Rack. He usado async_sinatra para construir una aplicación de encuestas largas, y ahora estoy experimentando con Rack de metal desnudo usando throw :async y / o Thin''s --thread flag. Me siento cómodo con el tema, pero hay algunas cosas que no puedo entender. (No, no estoy confundiendo la concurrencia para el paralelismo, y sí, entiendo las limitaciones impuestas por el GIL).

Q1. Mis pruebas indican que thin --threaded - rack.multithread=true (es decir, rack.multithread=true ) ejecuta solicitudes al mismo tiempo en hilos separados (supongo que usando EM), lo que significa que la solicitud de larga ejecución A no bloqueará la solicitud B (IO a un lado). Esto significa que mi aplicación no requiere ninguna codificación especial (por ejemplo, devoluciones de llamada) para lograr la simultaneidad (una vez más, se ignora el bloqueo de llamadas DB, IO, etc.). Esto es lo que creo que he observado, ¿es correcto?

Q2. Hay otro medio más discutido para lograr concurrencia, involucrando EventMachine.defer y throw :async . Estrictamente hablando, las solicitudes no se manejan con hilos. Se tratan en serie, pero pasan su trabajo pesado y una devolución de llamada a EventMachine, que usa async.callback para enviar una respuesta en otro momento. Después de que la solicitud A haya descargado su trabajo a EM.defer, se inicia la solicitud B. ¿Es esto correcto?

Q3. Suponiendo que lo anterior sea más o menos correcto, ¿hay alguna ventaja particular para un método sobre el otro? Obviamente, --threaded parece una bala mágica. ¿Hay algún inconveniente? Si no, ¿por qué todos hablan de async_sinatra / throw :async / async.callback ? Quizás el primero es "Quiero hacer que mi aplicación Rails sea un poco más ágil bajo una gran carga" y esta última es más adecuada para aplicaciones con muchas solicitudes de larga ejecución. O tal vez la escala es un factor? Solo adivinando aquí.

Estoy ejecutando Thin 1.2.11 en MRI Ruby 1.9.2. (Para su información, tengo que usar la bandera --no-epoll , ya que hay un problema de larga data, supuestamente resuelto, pero no muy real, con el uso de epoll y Ruby 1.9.2 de EventMachine. Eso no viene al caso, pero cualquier visión es bienvenida.)