Más en rubyonrails.org: Más Ruby on Rails

NO LEA ESTE ARCHIVO EN GITHUB, LAS GUÍAS SE PUBLICAN EN https://railsguides.es/

Almacenamiento en Caché con Rails Descripción General

Esta guía es una introducción para acelerar su aplicación Rails con almacenamiento en caché.

El almacenamiento en caché significa almacenar el contenido generado durante el ciclo de solicitud-respuesta y para reutilizarlo al responder a solicitudes similares.

El almacenamiento en caché suele ser la forma más eficaz de mejorar el rendimiento de una aplicación. Mediante el almacenamiento en caché, los sitios web se ejecutan en un solo servidor con una sola base de datos puede soportar una carga de miles de usuarios concurrentes.

Rails proporciona un conjunto de funciones de almacenamiento en caché listas para usar. Esta guía le enseñará usted el alcance y finalidad de cada uno de ellos. Domina estas técnicas y tu Las aplicaciones de Rails pueden ofrecer millones de vistas sin tiempos de respuesta exorbitantes. o facturas del servidor.

Después de leer esta guía, sabrá:

1 Basic Caching

Esta es una introducción a tres tipos de técnicas de almacenamiento en caché: página, acción y almacenamiento en caché de fragmentos. De forma predeterminada, Rails proporciona almacenamiento en caché de fragmentos. Para usar caché de páginas y acciones, deberá agregar actionpack-page_caching y actionpack-action_caching a suGemfile.

De forma predeterminada, el almacenamiento en caché solo está habilitado en su entorno de producción. Jugar con el almacenamiento en caché local, querrá habilitar el almacenamiento en caché en su entorno estableciendo config.action_controller.perform_caching en true en el archivo config/environment/*.rb relevante:

config.action_controller.perform_caching = true

Cambiar el valor de config.action_controller.perform_caching solo tienen un efecto en el almacenamiento en caché proporcionado por el componente Action Controller. Por ejemplo, no afectará el almacenamiento en caché de bajo nivel, que abordamos abajo.

1.1 Page Caching

El almacenamiento en caché de páginas es un mecanismo de Rails que permite la solicitud de una página generada para ser cumplido por el servidor web (es decir, Apache o NGINX) sin tener que ir a través de toda la pila de Rails. Si bien esto es súper rápido, no se puede aplicar a cada situación (como páginas que necesitan autenticación). Además, porque el El servidor web está sirviendo un archivo directamente desde el sistema de archivos que necesitará implementar la caducidad de la caché.

El almacenamiento en caché de páginas se ha eliminado de Rails 4. Consulte la actionpack-page_caching gem.

1.2 Action Caching

El almacenamiento en caché de página no se puede utilizar para acciones que tienen filtros anteriores, por ejemplo, páginas que requieren autenticación. Aquí es donde entra en juego el almacenamiento en caché de acciones. El almacenamiento en caché de acciones funciona como el almacenamiento en caché de páginas, excepto que la solicitud web entrante llega a la pila de Rails para que antes de que se puedan ejecutar los filtros antes de que se sirva la caché. Esto permite que la autenticación y otras restricciones se ejecuten sin dejar de ofrecer el resultado de la salida de una copia en caché.

Action Caching se ha eliminado de Rails 4. Consulte la actionpack-action_caching gem. Consulte la DHH's key-based cache expiration overview para el método recientemente preferido.

1.3 Fragment Caching

Las aplicaciones web dinámicas suelen crear páginas con una variedad de componentes que no todos los cuales tienen las mismas características de almacenamiento en caché. Cuando diferentes partes del La página debe almacenarse en caché y caducar por separado, puede usar el almacenamiento en caché de fragmentos.

El almacenamiento en caché de fragmentos permite que un fragmento de la lógica de vista se envuelva en un bloque de caché y se sirva fuera del almacén de caché cuando llegue la siguiente solicitud.

Por ejemplo, si desea almacenar en caché cada producto en una página, puede usar este código:

<% @products.each do |product| %>
  <% cache product do %>
    <%= render product %>
  <% end %>
<% end %>

Cuando su aplicación reciba su primera solicitud a esta página, Rails escribirá una nueva entrada de caché con una clave única. Una clave se parece a esto:

views/products/index:bea67108094918eeba42cd4a6e786901/products/1

La cadena de caracteres en el medio es un resumen de árbol de plantilla. Es un hash resumen calculado en función del contenido del fragmento de vista que está almacenando en caché. Si cambia el fragmento de vista (por ejemplo, el HTML cambia), el resumen cambiará, caducando el archivo existente.

Una versión de caché, derivada del registro del producto, se almacena en la entrada de caché. Cuando se toca el producto, la versión de la caché cambia y los fragmentos almacenados que contienen la versión anterior se ignoran.

las tiendas de caché como Memcached eliminarán automáticamente los archivos de caché antiguos.

Si desea almacenar en caché un fragmento en determinadas condiciones, puede utilizar cache_if o cache_unless:

<% cache_if admin?, product do %>
  <%= render product %>
<% end %>
1.3.1 Collection caching

El ayudante render también puede almacenar en caché plantillas individuales renderizadas para una colección. Incluso puede mejorar el ejemplo anterior con each leyendo todo el caché plantillas a la vez en lugar de una por una. Esto se hace pasando cached: true al renderizar la colección:

<%= render partial: 'products/product', collection: @products, cached: true %>

Todas las plantillas almacenadas en caché de renderizaciones anteriores se recuperarán a la vez con mucho mayor velocidad. Además, las plantillas que aún no se hayan almacenado en caché serán escrito en la caché y recuperado en el siguiente render.

1.4 Russian Doll Caching

Es posible que desee anidar fragmentos almacenados en caché dentro de otros fragmentos almacenados en caché. Esto es llamado caché de muñecas rusas.

La ventaja del almacenamiento en caché de muñecas rusas es que si se actualiza un solo producto, todos los demás fragmentos internos se pueden reutilizar al regenerar el exterior fragmento.

Como se explicó en la sección anterior, un archivo en caché caducará si el valor de updated_at cambia para un registro del que depende directamente el archivo en caché. Sin embargo, esto no caducará ningún caché en el que esté anidado el fragmento.

Por ejemplo, adopte la siguiente perspectiva:

<% cache product do %>
  <%= render product.games %>
<% end %>

Lo que a su vez muestra esta vista:

<% cache game do %>
  <%= render game %>
<% end %>

Si se cambia cualquier atributo del juego, el valor de updated_at se establecerá en el tiempo actual, con lo que expira el caché. Sin embargo, debido a que updated_at no se cambiará para el objeto del producto, esa caché no caducará y su aplicación servirá datos obsoletos. Para solucionar este problema, vinculamos los modelos con el método touch:

class Product < ApplicationRecord
  has_many :games
end

class Game < ApplicationRecord
  belongs_to :product, touch: true
end

Con touch establecido entrue, cualquier acción que cambie updated_at para un juego registro también lo cambiará para el producto asociado, con lo que expirará el cache.

1.5 Shared Partial Caching

Es posible compartir parciales y el almacenamiento en caché asociado entre archivos con diferentes tipos de mime. Por ejemplo, el almacenamiento en caché parcial compartido permite a los escritores de plantillas compartir un parcial entre archivos HTML y JavaScript. Cuando las plantillas se recopilan en las rutas del archivo de resolución de plantillas, solo incluyen la extensión del idioma de la plantilla y no el tipo mime. Debido a esto, las plantillas se pueden usar para múltiples tipos de mímica. Tanto las solicitudes HTML como JavaScript responderán al siguiente código:

render(partial: 'hotels/hotel', collection: @hotels, cached: true)

Cargará un archivo llamado hotels/hotel.erb.

Otra opción es incluir el nombre de archivo completo del parcial a renderizar.

render(partial: 'hotels/hotel.html.erb', collection: @hotels, cached: true)

Cargará un archivo llamado hotels/hotel.html.erb en cualquier tipo de archivo mime, por ejemplo, podría incluir este parcial en un archivo JavaScript.

1.6 Managing dependencies

Para invalidar correctamente la caché, debe definir correctamente la almacenamiento en caché de dependencias. Rails es lo suficientemente inteligente como para manejar casos comunes para que usted no tengo que especificar algo. Sin embargo, a veces, cuando se trata de helpers, por ejemplo, debe definirlos explícitamente.

1.6.1 Implicit dependencies

La mayoría de las dependencias de la plantilla se pueden derivar de llamadas a render en la plantilla sí mismo. A continuación se muestran algunos ejemplos de llamadas de renderización que ActionView::Digestor conoce cómo decodificar:

render partial: "comments/comment", collection: commentable.comments
render "comments/comments"
render 'comments/comments'
render('comments/comments')

render "header" translates to render("comments/header")

render(@topic)         translates to render("topics/topic")
render(topics)         translates to render("topics/topic")
render(message.topics) translates to render("topics/topic")

Por otro lado, algunas llamadas deben cambiarse para que el almacenamiento en caché funcione correctamente. Por ejemplo, si está pasando una colección personalizada, deberá cambiar:

render @project.documents.where(published: true)

a:

render partial: "documents/document", collection: @project.documents.where(published: true)
1.6.2 Explicit dependencies

A veces tendrá dependencias de plantilla que no se pueden derivar en absoluto. Esta Suele ser el caso cuando la renderización ocurre en ayudantes. He aquí un ejemplo:

<%= render_sortable_todolists @project.todolists %>

Deberá usar un formato de comentario especial para llamarlos:

<%# Template Dependency: todolists/todolist %>
<%= render_sortable_todolists @project.todolists %>

En algunos casos, como una configuración de herencia de una sola tabla, es posible que tenga un montón de dependencias explícitas. En lugar de escribir todas las plantillas, puede utilizar un comodín para que coincida con cualquier plantilla en un directorio:

<%# Template Dependency: events/* %>
<%= render_categorizable_events @person.events %>

En cuanto al almacenamiento en caché de la colección, si la plantilla parcial no comienza con una limpieza llamada de caché, aún puede beneficiarse del almacenamiento en caché de la colección agregando un formato de comentario en cualquier lugar de la plantilla, como:

<%# Template Collection: notification %>
<% my_helper_that_calls_cache(some_arg, notification) do %>
  <%= notification.name %>
<% end %>
1.6.3 External dependencies

Si usa un método auxiliar, por ejemplo, dentro de un bloque en caché y luego actualiza ese ayudante, tendrás que aumentar el caché también. Realmente no importa como lo hace, pero el MD5 del archivo de plantilla debe cambiar. Una recomendación es simplemente sea explícito en un comentario, como:

<%# Helper Dependency Updated: Jul 28, 2015 at 7pm %>
<%= some_helper_method(person) %>

1.7 Low-Level Caching

A veces es necesario almacenar en caché un valor particular o el resultado de una consulta en lugar de almacenar en caché fragmentos de vista. El mecanismo de almacenamiento en caché de Rails funciona muy bien para almacenar cualquier tipo de información.

La forma más eficiente de implementar el almacenamiento en caché de bajo nivel es utilizando el método Rails.cache.fetch. Este método lee y escribe en la caché. Cuando se pasa un solo argumento, se obtiene la clave y se devuelve el valor de la caché. Si se pasa un bloque, ese bloque se ejecutará en caso de que falle la caché. El valor de retorno del bloque se escribirá en la caché bajo la clave de caché dada, y se devolverá ese valor de retorno. En caso de acierto en la caché, el valor almacenado en caché se devolverá sin ejecutar el bloque.

Considere el siguiente ejemplo. Una aplicación tiene un modelo de "Producto" con un método de instancia que busca el precio del producto en un sitio web de la competencia. Los datos devueltos por este método serían perfectos para el almacenamiento en caché de bajo nivel:

class Product < ApplicationRecord
  def competing_price
    Rails.cache.fetch("#{cache_key_with_version}/competing_price", expires_in: 12.hours) do
      Competitor::API.find_price(id)
    end
  end
end

Observe que en este ejemplo usamos el método cache_key_with_version, por lo que la clave de caché resultante será algo como products/233-20140225082222765838000/competing_price. cache_key_with_version genera una cadena basada en el nombre de clase del modelo,id y atributos updated_at. Esta es una convención común y tiene la ventaja de invalidar la caché cada vez que se actualiza el producto. En general, cuando utiliza el almacenamiento en caché de bajo nivel para la información de nivel de instancia, debe generar una clave de caché.

1.8 SQL Caching

El almacenamiento en caché de consultas es una función de Rails que almacena en caché el conjunto de resultados devuelto por cada consulta. Si Rails vuelve a encontrar la misma consulta para esa solicitud, utilizará el conjunto de resultados en caché en lugar de ejecutar la consulta en la base de datos de nuevo.

Por ejemplo:

class ProductsController < ApplicationController

  def index
    # Run a find query
    @products = Product.all

    ...

    # Run the same query again
    @products = Product.all
  end

end

La segunda vez que se ejecuta la misma consulta en la base de datos, en realidad no llegará a la base de datos. La primera vez que se devuelve el resultado de la consulta, se almacena en la caché de consultas (en la memoria) y la segunda vez se extrae de la memoria.

Sin embargo, es importante tener en cuenta que los cachés de consultas se crean al comienzo de una acción y destruida al final de esa acción y, por lo tanto, persisten solo durante el duración de la acción. Si desea almacenar los resultados de la consulta en un formato más de manera persistente, puede hacerlo con almacenamiento en caché de bajo nivel.

2 Cache Stores

Rails proporciona diferentes almacenes para los datos almacenados en caché (además de SQL y page almacenamiento en caché).

2.1 Configuration

Puede configurar el almacén de caché predeterminado de su aplicación configurando el Opción de configuración config.cache_store. Otros parámetros se pueden pasar como argumentos para el constructor de la tienda de caché:

config.cache_store = :memory_store, { size: 64.megabytes }

Alternativamente, puede llamar a ActionController::Base.cache_store fuera de un bloque de configuración.

Puede acceder a la caché llamando a Rails.cache.

2.2 ActiveSupport::Cache::Store

Esta clase proporciona la base para interactuar con la caché en Rails. Esta es una clase abstracta y no puede usarla por sí sola. En su lugar, debe utilizar una implementación concreta de la clase vinculada a un motor de almacenamiento. Rails se envía con varias implementaciones documentadas a continuación.

Los métodos principales para llamar son read, write, delete, exist?, y fetch. El método de búsqueda toma un bloque y devolverá un valor existente de la caché o evaluará el bloque y escribirá el resultado en la caché si no existe ningún valor.

Hay algunas opciones comunes que pueden ser utilizadas por todas las implementaciones de caché. Estos se pueden pasar al constructor oa los diversos métodos para interactuar con las entradas.

  • :namespace: esta opción se puede usar para crear un espacio de nombres dentro del almacén de caché. Es especialmente útil si su aplicación comparte un caché con otras aplicaciones.

  • :compress: habilitado de forma predeterminada. Comprime las entradas de caché para que se puedan almacenar más datos en el mismo espacio de memoria, lo que genera menos desalojos de caché y mayores tasas de aciertos.

  • :compress_threshold: el valor predeterminado es 1kB. Las entradas de caché mayores que este umbral, especificadas en bytes, se comprimen.

  • :expires_in - Esta opción establece un tiempo de vencimiento en segundos para la entrada de la caché, si el almacén de caché lo admite, cuando se eliminará automáticamente de la caché.

  • :race_condition_ttl: esta opción se utiliza junto con la opción :expires_in. Evitará las condiciones de carrera cuando las entradas de la caché caduquen al evitar que múltiples procesos regeneren simultáneamente la misma entrada (también conocido como efecto de pila de perros). Esta opción establece el número de segundos que se puede reutilizar una entrada vencida mientras se regenera un nuevo valor. Es una buena práctica establecer este valor si usa la opción :expires_in.

2.2.1 Connection Pool Options

Por defecto, MemCacheStore yRedisCacheStore usan una sola conexión por proceso. Esto significa que si está utilizando Puma u otro servidor con subprocesos, puede tener varios subprocesos esperando a que la conexión esté disponible. Para aumentar la cantidad de conexiones disponibles, puede habilitar la conexión agrupación.

Primero, agregue la gema connection_pool a su Gemfile:

gem 'connection_pool'

A continuación, pase las opciones :pool_size y / o :pool_timeout al configurar el almacén de caché:

config.cache_store = :mem_cache_store, "cache.example.com", { pool_size: 5, pool_timeout: 5 }
  • :pool_size: esta opción establece el número de conexiones por proceso (el valor predeterminado es 5).

  • :pool_timeout: esta opción establece el número de segundos de espera para una conexión (el valor predeterminado es 5). Si no hay conexión disponible dentro del tiempo de espera, se generará un Timeout::Error.

2.2.2 Custom Cache Stores

Puede crear su propio almacén de caché personalizado simplemente extendiendo ActiveSupport::Cache::Store e implementando los métodos apropiados. De esta manera, puede intercambiar cualquier cantidad de tecnologías de almacenamiento en caché en su aplicación Rails.

Para usar un almacén de caché personalizado, simplemente configure el almacén de caché en una nueva instancia de su clase personalizada.

config.cache_store = MyCacheStore.new

2.3 ActiveSupport::Cache::MemoryStore

Este almacén de caché mantiene las entradas en la memoria en el mismo proceso de Ruby. El caché tienda tiene un tamaño acotado especificado enviando la opción :size al inicializador (el valor predeterminado es 32 Mb). Cuando la caché excede el tamaño asignado, un se producirá la limpieza y las entradas utilizadas menos recientemente b

config.cache_store = :memory_store, { size: 64.megabytes }

Si está ejecutando varios procesos del servidor Ruby on Rails (que es el caso si está usando Phusion Passenger o el modo en clúster de Puma), entonces su servidor Rails Las instancias de proceso no podrán compartir datos de caché entre sí. Este caché store no es apropiado para grandes implementaciones de aplicaciones. Sin embargo, puede funcionan bien para sitios pequeños y de poco tráfico con solo un par de procesos de servidor, así como entornos de desarrollo y prueba.

Los nuevos proyectos de Rails están configurados para utilizar esta implementación en el entorno de desarrollo de forma predeterminada.

Dado que los procesos no compartirán datos de caché al usar :memory_store, no será posible leer, escribir o caducar manualmente el caché a través de la consola Rails.

2.4 ActiveSupport::Cache::FileStore

Este almacén de caché utiliza el sistema de archivos para almacenar entradas. La ruta al directorio donde se almacenarán los archivos de la tienda debe especificarse al inicializar la caché.

config.cache_store = :file_store, "/path/to/cache/directory"

Con este almacén de caché, varios procesos de servidor en el mismo host pueden compartir un cache. Este almacén de caché es apropiado para sitios de tráfico bajo a medio que son servido de uno o dos anfitriones. Los procesos del servidor que se ejecutan en diferentes hosts comparta una caché mediante un sistema de archivos compartido, pero no se recomienda esa configuración.

Como la caché crecerá hasta que el disco esté lleno, se recomienda borre periódicamente las entradas antiguas.

Esta es la implementación de la tienda de caché predeterminada (en "#{root}/tmp/cache/") si no se proporciona un config.cache_store explícito.

2.5 ActiveSupport::Cache::MemCacheStore

Este almacén de caché utiliza el servidor "memcached" de Danga para proporcionar un caché centralizado para su aplicación. Rails usa la gema dalli incluida por defecto. Esta es actualmente la tienda de caché más popular para sitios web de producción. Se puede utilizar para proporcionar un único clúster de caché compartido con un rendimiento y una redundancia muy altos.

Al inicializar la caché, debe especificar las direcciones para todos servidores memcached en su clúster. Si no se especifica ninguno, asumirá memcached se está ejecutando en localhost en el puerto predeterminado, pero este no es un ideal configuración para sitios más grandes.

Los métodos write y fetch en esta caché aceptan dos opciones adicionales que aprovechan las características específicas de Memcached. Puede especificar :raw para enviar un valor directamente al servidor sin serialización. El valor debe ser una cadena o un número. Puede usar operaciones directas de Memcached como increment y decrement solo en valores sin procesar. También puede especificar :less_exist si no desea que memcached sobrescriba una entrada existente.

config.cache_store = :mem_cache_store, "cache-1.example.com", "cache-2.example.com"

2.6 ActiveSupport::Cache::RedisCacheStore

La tienda de caché de Redis aprovecha el soporte de Redis para el desalojo automático cuando alcanza la memoria máxima, lo que le permite comportarse como un servidor de caché Memcached.

Nota de implementación: Redis no expira las claves de forma predeterminada, así que tenga cuidado de usar un servidor de caché Redis dedicado. No llene su servidor Redis persistente con datos de caché volátiles! Leer el Guía de configuración del servidor de caché de Redis en detalle.

Para un servidor Redis de solo caché, configure maxmemory-policy en una de las variantes de allkeys. Redis 4+ admite el desalojo menos utilizado (allkeys-lfu), un excelente elección predeterminada. Redis 3 y versiones anteriores deberían utilizar el desalojo utilizado menos recientemente (allkeys-lru).

Establezca tiempos de espera de lectura y escritura en caché relativamente bajos. Regenerando un valor en caché suele ser más rápido que esperar más de un segundo para recuperarlo. Ambos leen y escribir tiempos de espera predeterminados a 1 segundo, pero se puede establecer más bajo si su red es constantemente de baja latencia.

De forma predeterminada, el almacén de caché no intentará volver a conectarse a Redis si el la conexión falla durante una solicitud. Si experimenta desconexiones frecuentes, es posible que desee habilitar intentos de reconexión.

Las lecturas y escrituras de caché nunca generan excepciones; simplemente devuelven nil en su lugar, comportarse como si no hubiera nada en el caché. Para medir si su caché es golpeando excepciones, puede proporcionar un error_handler para informar a un servicio de recopilación de excepciones. Debe aceptar tres argumentos de palabras clave: método, el método de almacenamiento de caché que se llamó originalmente; return, el valor que se devolvió al usuario, normalmente nil; y excepción, la excepción que fue rescatado.

Para comenzar, agregue la gema redis a su Gemfile:

gem 'redis'

Puede habilitar el soporte para más rápido hiredis biblioteca de conexión agregando adicionalmente su envoltorio ruby a su Gemfile:

gem 'hiredis'

La tienda de caché de Redis requerirá y usará automáticamente hiredis si está disponible. No más se necesita configuración.

Finalmente, agregue la configuración en el archivo config/environment/*.rb relevante:

config.cache_store = :redis_cache_store, { url: ENV['REDIS_URL'] }

Un almacén de caché de Redis de producción más complejo puede verse así:

cache_servers = %w(redis://cache-01:6379/0 redis://cache-02:6379/0)
config.cache_store = :redis_cache_store, { url: cache_servers,

  connect_timeout:    30,  # Defaults to 20 seconds
  read_timeout:       0.2, # Defaults to 1 second
  write_timeout:      0.2, # Defaults to 1 second
  reconnect_attempts: 1,   # Defaults to 0

  error_handler: -> (method:, returning:, exception:) {
    # Report errors to Sentry as warnings
    Raven.capture_exception exception, level: 'warning',
      tags: { method: method, returning: returning }
  }
}

2.7 ActiveSupport::Cache::NullStore

Esta implementación de almacenamiento de caché está destinada a usarse solo en entornos de desarrollo o prueba y nunca almacena nada. Esto puede ser muy útil en el desarrollo cuando tiene código que interactúa directamente con Rails.cache pero el almacenamiento en caché puede interferir con la posibilidad de ver los resultados de los cambios de código. Con este almacén de caché, todas las operaciones de fetch y read darán como resultado un error.

config.cache_store = :null_store

3 Cache Keys

Las claves utilizadas en una caché pueden ser cualquier objeto que responda a cache_key o to_param. Puede implementar el método cache_key en sus clases si lo necesita para generar claves personalizadas. Active Record generará claves basadas en el nombre de la clase y registro de identificación.

Puede utilizar hashes y matrices de valores como claves de caché.

# This is a legal cache key
Rails.cache.read(site: "mysite", owners: [owner_1, owner_2])

Las claves que use en Rails.cache no serán las mismas que las que realmente usa con el motor de almacenamiento. Pueden modificarse con un espacio de nombres o modificarse para adaptarse limitaciones de la tecnología backend. Esto significa, por ejemplo, que no puede guardar valores con Rails.cache y luego intente extraerlos con la gema dalli. Sin embargo, tampoco debe preocuparse por exceder el límite de tamaño de Memcached o violar las reglas de sintaxis.

4 Conditional GET support

Los GET condicionales son una característica de la especificación HTTP que proporciona una forma para que los servidores web le digan a los navegadores que la respuesta a una solicitud GET no ha cambiado desde la última solicitud y se puede extraer de forma segura de la caché del navegador.

Funcionan utilizando los encabezados HTTP_IF_NONE_MATCH y HTTP_IF_MODIFIED_SINCE para pasar tanto un identificador de contenido único como la marca de tiempo de la última vez que se cambió el contenido. Si el navegador realiza una solicitud donde el identificador de contenido (etag) o la última modificación desde la marca de tiempo coincide con la versión del servidor, entonces el servidor solo necesita enviar una respuesta vacía con un estado no modificado.

Es responsabilidad del servidor (es decir, nuestra) buscar la última marca de tiempo modificada y el encabezado if-none-match y determinar si enviar o no la respuesta completa. Con el soporte de obtención condicional en Rails, esta es una tarea bastante fácil:

class ProductsController < ApplicationController

  def show
    @product = Product.find(params[:id])

    # If the request is stale according to the given timestamp and etag value
    # (i.e. it needs to be processed again) then execute this block
    if stale?(last_modified: @product.updated_at.utc, etag: @product.cache_key_with_version)
      respond_to do |wants|
        # ... normal response processing
      end
    end

    # If the request is fresh (i.e. it's not modified) then you don't need to do
    # anything. The default render checks for this using the parameters
    # used in the previous call to stale? and will automatically send a
    # :not_modified. So that's it, you're done.
  end
end

En lugar de un hash de opciones, también puede simplemente pasar un modelo. Rails usará los métodos updated_at y cache_key_with_version para configurar last_modified y etag:

class ProductsController < ApplicationController
  def show
    @product = Product.find(params[:id])

    if stale?(@product)
      respond_to do |wants|
        # ... normal response processing
      end
    end
  end
end

Si no tiene ningún procesamiento de respuesta especial y está usando el mecanismo de renderizado predeterminado (es decir, no está usando respond_to o llamando a renderizar usted mismo), entonces tiene un ayudante fácil en fresh_when:

class ProductsController < ApplicationController

  # This will automatically send back a :not_modified if the request is fresh,
  # and will render the default template (product.*) if it's stale.

  def show
    @product = Product.find(params[:id])
    fresh_when last_modified: @product.published_at.utc, etag: @product
  end
end

A veces queremos almacenar en caché la respuesta, por ejemplo, una página estática, que nunca obtiene Caducado. Para lograr esto, podemos usar el ayudante http_cache_forever y haciendo por lo que el navegador y los proxies lo almacenarán en caché indefinidamente.

De forma predeterminada, las respuestas almacenadas en caché serán privadas, almacenadas en caché solo en la web del usuario. navegador. Para permitir que los proxies almacenen en caché la respuesta, establezca public: true para indicar que pueden entregar la respuesta en caché a todos los usuarios.

Con este ayudante, el encabezado last_modified se establece en Time.new(2011, 1, 1).utc y el encabezado expires se establece en 100 años.

Utilice este método con cuidado ya que el navegador / proxy no podrá invalidar la respuesta almacenada en caché a menos que la caché del navegador se borre a la fuerza.

class HomeController < ApplicationController
  def index
    http_cache_forever(public: true) do
      render
    end
  end
end

4.1 Strong v/s Weak ETags

Rails genera ETag débiles de forma predeterminada. Los ETag débiles permiten semánticamente equivalentes respuestas para tener los mismos ETag, incluso si sus cuerpos no coinciden exactamente. Esto es útil cuando no queremos que la página se vuelva a generar para cambios menores en cuerpo de respuesta.

Los ETag débiles tienen un W / principal para diferenciarlos de los ETag fuertes.

  W/"618bbc92e2d35ea1945008b42799b0e7" → Weak ETag
  "618bbc92e2d35ea1945008b42799b0e7" → Strong ETag

A diferencia de ETag débil, ETag fuerte implica que la respuesta debe ser exactamente la misma y byte a byte idéntico. Útil cuando se realizan solicitudes de rango dentro de un archivo grande de video o PDF. Algunas CDN solo admiten ETag potentes, como Akamai. Si es absolutamente necesario generar un ETag fuerte, puede hacerlo de la siguiente manera.

  class ProductsController < ApplicationController
    def show
      @product = Product.find(params[:id])
      fresh_when last_modified: @product.published_at.utc, strong_etag: @product
    end
  end

También puede configurar el ETag fuerte directamente en la respuesta.

  response.strong_etag = response.body # => "618bbc92e2d35ea1945008b42799b0e7"

5 Caching in Development

Es común querer probar la estrategia de almacenamiento en caché de su aplicación en modo de desarrollo. Rails proporciona el comando rails dev:cache para activar / desactivar fácilmente el almacenamiento en caché.

$ bin/rails dev:cache
Development mode is now being cached.
$ bin/rails dev:cache
Development mode is no longer being cached.

6 References

Comentarios Sobre el Contenido

Las guías de rieles se administran y publican en latinadeveloper/railsguides.es en GitHub.

Si lee esta guía y encuentra algún texto o código incorrecto que le interese, no dude en enviar una solicitud de extracción en el repositorio anterior. Consulte el archivo README en GitHub para saber cómo enviar una solicitud de extracción. Please contribute if you see any typos or factual errors.