¿Rails no comparte nada o las solicitudes separadas pueden acceder a las mismas variables de tiempo de ejecución?

StackOverflow https://stackoverflow.com/questions/1025432

Pregunta

PHP se ejecuta en un entorno de nada compartido, lo que en este contexto significa que cada solicitud web se ejecuta en un entorno limpio. No puede acceder a los datos de otra solicitud, excepto a través de una capa de persistencia separada (sistema de archivos, base de datos, etc.).

¿Qué pasa con Ruby on Rails? Acabo de leer una publicación de blog indicando que solicitudes separadas pueden acceder a la misma variable de clase.

Se me ha ocurrido que esto probablemente depende del servidor web. Preguntas frecuentes de Mongrel establece que Mongrel usa un hilo por solicitud, lo que sugiere un entorno de nada compartido. Las preguntas frecuentes continúan diciendo que RoR no es seguro para subprocesos, lo que sugiere que RoR no existiría en un entorno compartido a menos que una nueva solicitud reutilice los objetos en memoria creados a partir de la solicitud anterior.

Obviamente, esto tiene enormes ramificaciones de seguridad. Entonces tengo dos preguntas:

  1. ¿El entorno RoR no se comparte?
  2. Si RoR se ejecuta (o podría ejecutarse en algunas circunstancias) en un entorno compartido, ¿sobre qué variables y otro almacenamiento de datos debería estar paranoico?

Actualización: aclararé más. en un contenedor de servlet de Java puede tener objetos que persisten en múltiples solicitudes. Esto normalmente se hace para almacenar en caché los datos a los que tendrían acceso múltiples usuarios, conexiones de bases de datos, etc. En PHP esto no se puede hacer en la capa de aplicación, se debe hacer en una capa de persistencia separada como Memcached. Entonces, la doble pregunta es: ¿qué escenario es similar a RoR (PHP o Java) y si es como Java, qué tipos de datos persisten en múltiples solicitudes?

¿Fue útil?

Solución

En resumen:

  1. No, Rails nunca se ejecuta en un entorno de nada compartido.
  2. Sea paranoico acerca de las variables de clase y las variables de instancia de clase .

La versión más larga:

Los procesos de Rails comienzan su ciclo de vida al cargar el marco y la aplicación. Por lo general, solo ejecutarán un único subproceso, que procesará muchas solicitudes durante su vida útil. Por lo tanto, las solicitudes se enviarán estrictamente secuencialmente .

Sin embargo, todas las clases persisten en todas las solicitudes. Esto significa que cualquier objeto al que se haga referencia desde sus clases y metaclases (como las variables de clase y las variables de instancia de clase) se compartirá entre las solicitudes. Esto puede morderte , por ejemplo, si intentas memorizar métodos ( @var || = expensive_calculation ) en tus métodos class , esperando que solo persistirá durante la solicitud actual. En realidad, el cálculo solo se realizará en la primera solicitud.

En la superficie, puede parecer agradable implementar el almacenamiento en caché u otro comportamiento que depende de la persistencia entre las solicitudes. Por lo general, no lo es. Esto se debe a que la mayoría de las estrategias de implementación utilizarán varios procesos de Rails para contrarrestar su propia naturaleza de subproceso único. Simplemente no es bueno bloquear todas las solicitudes mientras se espera una consulta lenta de la base de datos, por lo que la salida fácil es generar más procesos. Naturalmente, estos procesos no comparten nada (excepto algo de memoria tal vez, que no notará). Esto puede morderte si guardas cosas en tus variables de clase o variables de instancia de clase durante las solicitudes . Entonces, de alguna manera, a veces las cosas parecen estar presentes, y otras parecen desaparecer. (En realidad, por supuesto, los datos pueden o no estar presentes en algunos procesos y ausentes en otros).

Algunas configuraciones de implementación (especialmente JRuby + Glassfish) son de hecho multiproceso. Rails es seguro para subprocesos, por lo que puede manejarlo. Pero su aplicación puede no ser segura para subprocesos. Todas las instancias de controlador se descartan después de cada solicitud, pero como sabemos, las clases se comparten. Esto puede morderte si pasas información en variables de clase o en variables de instancia de clase. Si no utiliza correctamente los métodos de sincronización, es muy posible que termine en condiciones de carrera infernales.


Como nota al margen: Rails generalmente se ejecuta en procesos de un solo subproceso porque la implementación del subproceso de Ruby es una mierda. Afortunadamente, las cosas están un poco mejor en Ruby 1.9. Y un lote mejor en JRuby.

Con ambas implementaciones de Ruby ganando popularidad, parece probable que las estrategias de implementación de Rails multiproceso también ganen popularidad y número. Es una buena idea escribir su aplicación teniendo en cuenta el despacho de solicitudes multiproceso.

Otros consejos

Aquí hay un ejemplo relativamente simple que ilustra lo que puede suceder si no tiene cuidado al modificar objetos compartidos.

  1. Cree un nuevo proyecto Rails: prueba de rails

  2. Cree un nuevo archivo lib / misc.rb y póngalo así:

    class Misc
      @xxx = 'Hello'
      def Misc.contents()
        return @xxx
      end
    end
    
  3. Crear un nuevo controlador: script ruby ??/ generar controlador Publicaciones índice
  4. Cambiar app / views / posts / index.html.erb para contener este código:

    <%
      require 'misc'; y = Misc.contents() ; y << ' (goodbye) '
    %>
    <pre><%= y %></pre>
    

    (Aquí es donde modificamos el objeto compartido implícitamente).

  5. Agregue rutas RESTful a config / routes.rb .
  6. Inicie el servidor ruby ??script / server y cargue la página / posts varias veces. Verá que el número de cadenas (adiós) aumenta en uno en cada recarga de página sucesiva.

En su implementación promedio con Passenger, es probable que tenga múltiples procesos de aplicaciones que no comparten nada entre ellos sino clases dentro de cada proceso que mantienen su estado (estático) de una solicitud a otra. Sin embargo, cada solicitud crea una nueva instancia de sus controladores.

Podría llamar a esto un grupo de entornos de estado compartido distintos.

Para usar su analogía Java, puede hacer el almacenamiento en caché y hacer que funcione de solicitud a solicitud, simplemente no puede suponer que estará disponible en cada solicitud.

Shared-nothing es a veces una buena idea. Pero no cuando tiene que cargar un marco de aplicación grande y un modelo de dominio grande y una gran cantidad de configuración en cada solicitud.

Para mayor eficiencia, Rails mantiene algunos datos disponibles en la memoria para compartirlos entre todas las solicitudes durante la vida útil de una aplicación. La mayoría de estos datos son de solo lectura, por lo que no debe preocuparse.

Cuando escriba su aplicación, evite escribir en objetos compartidos (excluyendo la base de datos, por ejemplo, que viene de fábrica con un buen control de concurrencia) y debería estar bien.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top