2011-06-08 25 views
44

¿Sinatra tiene varios hilos? Leí más sobre dónde ese "sinatra tiene varios subprocesos por defecto", ¿qué implica eso?¿Sinatra tiene múltiples hilos?

Considere este ejemplo

get "/multithread" do 
    t1 = Thread.new{ 
    puts "sleeping for 10 sec" 
    sleep 10 
    # Actually make a call to Third party API using HTTP NET or whatever. 
    } 
    t1.join 
    "multi thread" 
end 

get "/dummy" do 
    "dummy" 
end 

Si accedo "/ multi-hilo" y "/ ficticio" posteriormente en otra pestaña o el navegador entonces nada puede ser servida (en este caso por 10 segundos) hasta "/ multi-hilo" la solicitud esta completa En caso de que la actividad se congele, la aplicación deja de responder.

¿Cómo podemos evitar esto sin generar otra instancia de la aplicación?

+1

Por supuesto, no puede continuar debido a '.join' que se bloqueará hasta que se termine cada subproceso - vea: http://ruby-doc.org/core-1.9/classes/Thread.html#M001331 – asaaki

+0

Bueno, eso fue un ejemplo, en realidad puedo estar haciendo una llamada para leer un archivo o un URI usando HTTP Net y no dentro de un hilo específicamente. ¿Qué sucede si no quiero que se bloquee otra solicitud? – ch4nd4n

+3

Sin generar más instancias, no veo ninguna solución fácil. Normalmente usaría thin o unicornio para tener instancias múltiples. Si solo desea tener algo de trabajo en segundo plano (por lo tanto, no importa si el resultado de la llamada al recurso externo se muestra inmediatamente), realmente debería usar trabajos en segundo plano (resque, trabajos retrasados, ...) y Si estos trabajos terminaron, los resultados se pueden mostrar en una solicitud adicional. El problema general es que, en la mayoría de los casos, las aplicaciones de Ruby no pueden ser realmente multiproceso, ya que la resonancia magnética aún no es compatible con varios núcleos. Engendrar/Bifurcar es una solución alternativa. – asaaki

Respuesta

91

tl; dr Sinatra funciona bien con Threads, pero es probable que deba utilizar un servidor web diferente.

Sinatra en sí mismo no impone ningún modelo de simultaneidad, ni siquiera maneja la simultaneidad. Esto lo hace el manejador de Rack (servidor web), como Thin, WEBrick o Passenger. Sinatra en sí mismo es seguro para subprocesos, lo que significa que si su controlador de Rack usa varios subprocesos para las solicitudes del servidor, funciona bien. Sin embargo, dado que Ruby 1.8 solo admite subprocesos verdes y Ruby 1.9 tiene un bloqueo de VM global, los subprocesos no son muy utilizados para la simultaneidad, ya que en ambas versiones, los subprocesos no se ejecutarán verdaderamente en paralelo. La voluntad, sin embargo, en JRuby o el próximo Rubinius 2.0 (ambas implementaciones alternativas de Ruby).

La mayoría de los racks existentes que usan hilos usarán un grupo de subprocesos para volver a utilizar los subprocesos en lugar de crear un subproceso para cada solicitud entrante, ya que la creación de subprocesos no es gratis, esp. en 1.9 donde los hilos mapean 1: 1 a hilos nativos. Los hilos verdes tienen mucha menos sobrecarga, razón por la cual las fibras, que básicamente son hilos verdes programados cooperativamente, tal como se utilizaba en la sinatra-sincronía antes mencionada, se volvieron tan populares recientemente. Debe tener en cuenta que cualquier comunicación de red tendrá que pasar por EventMachine, por lo que no puede usar la gema mysql, por ejemplo, para hablar con su base de datos.

Las fibras se adaptan bien para el procesamiento intensivo de la red, pero fallan miserablemente en cálculos pesados. Es menos probable que te encuentres en condiciones de carrera, una trampa común con concurrencia, si usas fibras, ya que solo hacen un cambio de contexto en puntos claramente definidos (con sincronización, cada vez que esperas IO). Hay un tercer modelo de concurrencia común: Procesos. Puede usar el servidor de precompresión o iniciar múltiples procesos usted mismo. Si bien esto parece una mala idea a primera vista, tiene algunas ventajas: en la implementación normal de Ruby, esta es la única forma de usar todas sus CPU al mismo tiempo. Y evita el estado compartido, por lo que no hay condiciones de carrera por definición. Además, las aplicaciones multiproceso se escalan fácilmente en varias máquinas. Tenga en cuenta que puede combinar procesos múltiples con otros modelos de concurrencia (evented, cooperative, preventive).

La elección se hace principalmente por el servidor y middleware que utilice:

  • multi-proceso, no preforking: Mestizo, Fino, WEBrick, Zbatery
  • Multi-Proceso, preforking: unicornio, arco iris , Pasajero
  • evented (adecuado para Sinatra-sincronía): Thin iris, Zbatery
  • Threaded: Net :: HTTP :: Server, roscado Mongrel, Puma, arco iris, Zbatery, Thin [1], Phusion Passenger Enterprise >= 4

[1] desde Sinatra 1.3.0, Thin se iniciará en modo enhebrado, si es iniciado por Sinatra (es decir, con ruby app.rb, pero no con el comando thin, ni con rackup).

+0

Gracias por aclarar las cosas. Buena explicación de los diferentes modelos. Como mencionaste en el tercer párrafo, los procesos son una buena solución y creo que se usan ampliamente para escalar aplicaciones./Subpregunta: ¿Qué configuración prefiere? – asaaki

+0

Gracias Konstantin por la elaboración. – ch4nd4n

+0

Por lo general, prefiero la preparación previa y, si le conviene a la infraestructura, uso IO con evented (con devoluciones de llamada, no con fibras). No tengo problemas con las devoluciones de llamada y no veo ventajas reales en sincronía-e/sinatra-synchrony, ya que no implementan futuros/promesas transparentes. Pero solo soy yo, supongo. –

7

Mientras que alrededor de google, encontramos esta joya:

sinatra-synchrony

que podría ayudar, porque toca usted pregunta.

También hay un punto de referencia, hicieron casi lo mismo que desea (llamadas externas).

Conclusión: ¡EventMachine es la respuesta aquí!

+1

Hice el punto de referencia, también. Mi resultado fue 26.6 segundos sin y 4.4 segundos con la gema sinatra-synchrony, ¡eso fue 16 veces más rápido! – asaaki

+0

Probaré esto y espero ponerte al día con tus datos. Gracias. – ch4nd4n

+0

Son 4 años más tarde, y parece que el autor odia EventMachine ahora: https://github.com/kyledrake/sinatra-synchrony –

1

Después de hacer algunos cambios en el código, pude ejecutar la aplicación padrino/sinatra en mizuno . Inicialmente traté de ejecutar la aplicación Padrino en jRuby pero era demasiado inestable y no investigué por qué. Estaba enfrentando bloqueos JVM cuando se ejecuta en jRuby. También revisé el artículo this, lo que me hace pensar por qué incluso elegir Ruby si la implementación puede ser cualquier cosa menos fácil.

¿Hay alguna discusión sobre el despliegue de aplicaciones en ruby? ¿O puedo generar un nuevo hilo :)

1

Llegué últimamente a JRuby y estoy muy sorprendido de lo simple que es pasar de MRI a JRuby. Prácticamente implica cambiar algunas gemas (en la mayoría de los casos).

Debería echar un vistazo a la combinación JRuby y Trinidad (servidor de aplicaciones). Torquebox también parece ser una solución todo en uno interesante, viene con mucho más que solo un servidor de aplicaciones.

Si desea tener un servidor de aplicaciones que admite el enhebrado, y esté familiarizado con Mongrel, Thin, Unicorn, etc., entonces Trinidad es probablemente la más fácil de migrar ya que es prácticamente idéntica desde la perspectiva de los usuarios. ¡Lo amo hasta ahora!

+0

Descubrí eso eventualmente. Por defecto thin es de un solo hilo, necesita habilitar el modo roscado al iniciar la aplicación. – ch4nd4n

+0

Creo que enhebrar en Delgadamente es experimental. Recuerdo a las personas que mencionaron que no es una buena idea usar Thin de esa manera, ya que es un servidor de aplicaciones con un gran avance. Sin embargo, recientemente Puma 1.0.0 fue lanzado y esta debería ser una opción interesante ahora. Hace mucho menos que Trinidad/TorqueBox y debería ser muy familiar para los Rubyistas. Además, funciona con MRI, JRuby y Rubinius, pero por supuesto obtener la mayor parte mediante una implementación de Ruby que permite el enhebrado verdadero como JRuby y Rubinius. –

4

Pensé que podría elaborar para las personas que se encuentran con esto. Sinatra incluye este pequeño trozo de código:

server.threaded = settings.threaded if server.respond_to? :threaded=  

Sinatra detectará qué joya que haya instalado un servidor web (también conocido como, delgada, el puma, lo que sea.) Y si responde a una "rosca" a configurarlo para ser roscado si se solicita. Ordenado.

Cuestiones relacionadas