Guía sobre el almacenamiento en caché con NGINX y NGINX Plus

NGINX | 23 de julio de 2015

Todos sabemos que el rendimiento de las aplicaciones y los sitios web es un factor decisivo para su éxito. Sin embargo, no siempre está claro cómo mejorar el rendimiento de una aplicación o un sitio web. La calidad del código y la infraestructura son, por supuesto, fundamentales, pero en muchos casos se puede mejorar enormemente la experiencia del usuario final centrándose en algunas técnicas muy básicas de entrega de aplicaciones. Un ejemplo de ello es implementar y optimizar el almacenamiento en caché en la pila de la aplicación. Esta entrada de blog aborda técnicas que pueden ayudar tanto a los usuarios principiantes como a los avanzados a obtener un mejor rendimiento mediante el uso de las funciones de caché de contenido incluidas en NGINX y NGINX Plus.

Descripción general

Una caché de contenido se sitúa entre un cliente y un «servidor de origen» y guarda copias de todo el contenido que recibe. Si un cliente solicita contenido almacenado en la caché, esta lo devuelve directamente sin contactar con el servidor de origen. Esto mejora el rendimiento, ya que la caché de contenido está más cerca del cliente, y permite utilizar los servidores de aplicaciones de forma más eficiente, pues no tienen que generar las páginas desde cero cada vez.

Puede haber varias cachés entre el navegador web y el servidor de aplicaciones: la caché del navegador del cliente, las cachés intermedias, las redes de distribución de contenidos (CDN) y el balanceador de carga o proxy inverso situado delante de los servidores de aplicaciones. El almacenamiento en caché, incluso en el nivel del proxy inverso o del balanceador de carga, puede mejorar considerablemente el rendimiento.

Por ejemplo, el año pasado asumí la tarea de optimizar el rendimiento de un sitio web que cargaba lentamente. Una de las primeras cosas que observé fue que se tardaba más de 1 segundo en generar la página de inicio principal. Tras investigar un poco, descubrí que, como la página estaba marcada para no almacenarse en caché, se generaba dinámicamente con cada solicitud. La página en sí no cambiaba con mucha frecuencia ni estaba personalizada, por lo que esto no era necesario. A modo de prueba, configuré el balanceador de carga para que almacenara la página de inicio en caché durante 5 segundos, y solo con eso se consiguió una mejora notable. El tiempo hasta el primer byte se redujo a unos pocos milisegundos y la página pasó a cargarse visiblemente más rápido.

NGINX suele implementarse como proxy inverso o equilibrador de carga en una pila de aplicaciones y cuenta con un conjunto completo de funciones de almacenamiento en caché. En la siguiente sección se explica cómo configurar el almacenamiento en caché básico con NGINX.

Cómo instalar y configurar el almacenamiento en caché básico

Solo se necesitan dos directivas para habilitar el almacenamiento en caché básico: proxy_cache_path y proxy_cache. La directiva proxy_cache_path establece la ruta y la configuración de la caché, y la directiva proxy_cache la activa.

proxy_cache_path /path/to/cache levels=1:2 keys_zone=my_cache:10m max_size=10g                  inactive=60m use_temp_path=off;

server {
    # ...
    location / {
        proxy_cache my_cache;
        proxy_pass http://my_upstream;
    }
}

Los parámetros de la directiva proxy_cache_path definen los siguientes ajustes:

  • El directorio del disco local para la caché se denomina /path/to/cache/.
  • levels configura una jerarquía de directorios de dos niveles en /path/to/cache/. Tener una gran cantidad de archivos en un único directorio puede ralentizar el acceso a los archivos, por lo que recomendamos una jerarquía de directorios de dos niveles para la mayoría de las implementaciones. Si no se incluye el parámetro levels, NGINX coloca todos los archivos en el mismo directorio.
  • keys_zone configura una zona de memoria compartida para almacenar las claves de caché y metadatos, como los temporizadores de uso. Al mantener una copia de las claves en memoria, NGINX puede determinar rápidamente si una solicitud es un HIT o un MISS sin tener que acceder al disco, lo que agiliza considerablemente la comprobación. Una zona de 1 MB puede almacenar datos de unas 8000 claves, por lo que la zona de 10 MB configurada en el ejemplo puede almacenar datos de unas 80 000 claves.
  • max_size establece el límite máximo del tamaño de la caché (10 gigabytes en este ejemplo). Es opcional; si no se especifica ningún valor, la caché puede crecer hasta ocupar todo el espacio disponible en disco. Cuando el tamaño de la caché alcanza el límite, un proceso denominado administrador de caché elimina los archivos utilizados menos recientemente para que el tamaño de la caché vuelva a situarse por debajo del límite.
  • inactive especifica cuánto tiempo puede permanecer un elemento en la caché sin que se acceda a él. En este ejemplo, el proceso de gestión de la caché elimina automáticamente de la caché cualquier archivo que no se haya solicitado durante 60 minutos, independientemente de si ha caducado o no. El valor predeterminado es de 10 minutos (10m). El contenido inactivo no es lo mismo que el contenido caducado. NGINX no elimina automáticamente el contenido que ha caducado según lo definido por una cabecera de control de caché (Cache-Control:max-age=120, por ejemplo). El contenido caducado (obsoleto) solo se elimina cuando no se ha accedido a él durante el tiempo especificado por inactive. Cuando se accede a contenido caducado, NGINX lo actualiza desde el servidor de origen y reinicia el temporizador de inactive.
  • NGINX escribe primero los archivos destinados a la caché en un área de almacenamiento temporal, y la directiva use_temp_path=off indica a NGINX que los escriba en los mismos directorios donde se almacenarán en caché. Recomendamos establecer este parámetro en off para evitar la copia innecesaria de datos entre sistemas de archivos. use_temp_path se introdujo en la versión 1.7.10 de NGINX y en NGINX Plus R6.

Y, por último, la directiva proxy_cache activa el almacenamiento en caché de todo el contenido que coincide con la URL del bloque location principal (en el ejemplo, /). También puede incluir la directiva proxy_cache en un bloque server; esta se aplica a todos los bloques location del servidor que no tengan su propia directiva proxy_cache.

Servir contenido en caché cuando el servidor de origen no está disponible

Una potente función del almacenamiento en caché de contenido de NGINX es que se puede configurar para servir contenido obsoleto desde su caché cuando no puede obtener contenido actualizado de los servidores de origen. Esto puede ocurrir si todos los servidores de origen de un recurso almacenado en caché están inactivos u ocupados temporalmente. En lugar de transmitir el error al cliente, NGINX sirve desde su caché la versión obsoleta del archivo. Esto proporciona un nivel adicional de tolerancia a fallos para los servidores a los que NGINX actúa como proxy y garantiza la disponibilidad en caso de fallos del servidor o picos de tráfico. Para habilitar esta funcionalidad, incluya la directiva proxy_cache_use_stale:

location / {    # ...
    proxy_cache_use_stale error timeout http_500 http_502 http_503 http_504;
}

Con esta configuración de ejemplo, si NGINX recibe un error, un timeout o cualquiera de los errores 5xx especificados del servidor de origen y tiene en la caché una versión obsoleta del archivo solicitado, entrega el archivo obsoleto en lugar de transmitir el error al cliente.

Ajuste preciso de la caché y mejora del rendimiento

NGINX ofrece una gran variedad de ajustes opcionales para optimizar el rendimiento de la caché. A continuación se muestra un ejemplo que activa algunos de ellos:

proxy_cache_path /path/to/cache levels=1:2 keys_zone=my_cache:10m max_size=10g                  inactive=60m use_temp_path=off;

server {
    # ...
    location / {
        proxy_cache my_cache;
        proxy_cache_revalidate on;
        proxy_cache_min_uses 3;
        proxy_cache_use_stale error timeout updating http_500 http_502
                              http_503 http_504;
        proxy_cache_background_update on;
        proxy_cache_lock on;

        proxy_pass http://my_upstream;
    }
}

Estas directivas configuran el siguiente comportamiento:

  • proxy_cache_revalidate indica a NGINX que utilice solicitudes GET condicionales al actualizar el contenido desde los servidores de origen. Si un cliente solicita un elemento almacenado en caché que ha caducado según lo definido por las cabeceras de control de caché, NGINX incluye el campo If-Modified-Since en la cabecera de la solicitud GET que envía al servidor de origen. Esto permite ahorrar ancho de banda, ya que el servidor solo envía el elemento completo si se ha modificado desde la fecha registrada en la cabecera Last-Modified adjunta al archivo cuando NGINX lo almacenó originalmente en caché.
  • proxy_cache_min_uses establece el número de veces que los clientes deben solicitar un elemento antes de que NGINX lo almacene en caché. Esto resulta útil si la caché se llena constantemente, ya que garantiza que solo se añadan a ella los elementos a los que se accede con mayor frecuencia. De forma predeterminada, proxy_cache_min_uses está establecido en 1.
  • El parámetro updating de la directiva proxy_cache_use_stale, junto con la activación de la directiva proxy_cache_background_update, indica a NGINX que entregue contenido obsoleto cuando los clientes soliciten un elemento que haya caducado o que se esté actualizando desde el servidor de origen. Todas las actualizaciones se realizarán en segundo plano. El archivo obsoleto se devolverá para todas las solicitudes hasta que el archivo actualizado se haya descargado por completo.
  • Con proxy_cache_lock habilitado, si varios clientes solicitan un archivo que no está actualizado en la caché (un MISS), solo se permite que la primera de esas solicitudes llegue al servidor de origen. Las solicitudes restantes esperan a que se complete esa solicitud y, a continuación, obtienen el archivo de la caché. Sin proxy_cache_lock habilitado, todas las solicitudes que producen fallos de caché van directamente al servidor de origen.

Distribución de la caché entre varios discos duros

Si dispone de varios discos duros, puede utilizar NGINX para distribuir la caché entre ellos. A continuación se muestra un ejemplo que distribuye los clientes de manera uniforme entre dos discos duros en función del URI de la solicitud:

proxy_cache_path /path/to/hdd1 levels=1:2 keys_zone=my_cache_hdd1:10m                 max_size=10g inactive=60m use_temp_path=off;
proxy_cache_path /path/to/hdd2 levels=1:2 keys_zone=my_cache_hdd2:10m
                 max_size=10g inactive=60m use_temp_path=off;

split_clients $request_uri $my_cache {
              50%          “my_cache_hdd1”;
              50%          “my_cache_hdd2”;
}

server {
    # ...
    location / {
        proxy_cache $my_cache;
        proxy_pass http://my_upstream;
    }
}

Las dos directivas proxy_cache_path definen dos cachés (my_cache_hdd1 y my_cache_hdd2) en dos discos duros diferentes. El bloque de configuración split_clients especifica que los resultados de la mitad de las solicitudes (50%) se almacenan en la caché my_cache_hdd1 y los de la otra mitad, en my_cache_hdd2. El hash basado en la variable $request_uri (el URI de la solicitud) determina qué caché se utiliza para cada solicitud, de modo que las solicitudes de un URI determinado siempre se almacenan en la misma caché.

Tenga en cuenta que este enfoque no sustituye a una configuración de discos duros en RAID. Si se produce un fallo en un disco duro, el sistema podría comportarse de forma impredecible; por ejemplo, los usuarios podrían recibir códigos de respuesta 500 en las solicitudes dirigidas al disco duro averiado. Una configuración RAID adecuada puede hacer frente a los fallos de los discos duros.

Preguntas frecuentes (FAQ)

Esta sección responde a algunas preguntas frecuentes sobre el almacenamiento en caché de contenido de NGINX.

¿Se puede instrumentar la caché de NGINX?

Sí, con la directiva «add_header»:

add_header X-Cache-Status $upstream_cache_status;

Este ejemplo añade una cabecera HTTP X-Cache-Status a las respuestas enviadas a los clientes. Estos son los posibles valores de $upstream_cache_status:

  • MISS – La respuesta no se encontró en la caché, por lo que se obtuvo de un servidor de origen. Es posible que después se almacenara en la caché.
  • BYPASS – La respuesta se obtuvo del servidor de origen en lugar de servirse desde la caché porque la solicitud coincidía con una directiva proxy_cache_bypass (consulte «¿Puedo excluir una parte de mi caché?» más abajo). Es posible que la respuesta se almacenara posteriormente en la caché.
  • EXPIRED – La entrada de la caché ha caducado. La respuesta contiene contenido actualizado del servidor de origen.
  • STALE – El contenido está obsoleto porque el servidor de origen no responde correctamente y se ha configurado proxy_cache_use_stale.
  • UPDATING – El contenido está obsoleto porque la entrada se está actualizando en respuesta a una solicitud anterior y se ha configurado proxy_cache_use_stale updating.
  • REVALIDATED – Se habilitó la directiva proxy_cache_revalidate y NGINX verificó que el contenido almacenado actualmente en caché seguía siendo válido (If-Modified-Since o If-None-Match).
  • HIT – La respuesta contiene contenido válido y actualizado procedente directamente de la caché.

¿Cómo determina NGINX si debe almacenar algo en caché?

NGINX almacena en caché una respuesta únicamente si el servidor de origen incluye la cabecera Expires con una fecha y hora futuras, o la cabecera Cache-Control con la directiva max-age establecida en un valor distinto de cero.

De forma predeterminada, NGINX respeta otras directivas de la cabecera Cache-Control: no almacena en caché las respuestas cuando la cabecera incluye la directiva Private, No-Cache o No-Store. Tampoco almacena en caché las respuestas que contienen la cabecera Set-Cookie. Además, solo almacena en caché las respuestas a solicitudes GET y HEAD. Puede modificar estos valores predeterminados como se describe en las respuestas que aparecen a continuación.

NGINX no almacena en caché las respuestas si proxy_buffering está configurado como off. De forma predeterminada, está configurado como on.

¿Se pueden ignorar las cabeceras «Cache-Control»?

Sí, con la directiva «proxy_ignore_headers». Por ejemplo, con esta configuración:

location /images/ {
    proxy_cache my_cache;
    proxy_ignore_headers Cache-Control;
    proxy_cache_valid any 30m;
    # ...
}

NGINX ignora la cabecera Cache-Control para todo lo que se encuentre en /images/. La directiva proxy_cache_valid establece una fecha de caducidad para los datos almacenados en caché y es necesaria si se ignoran las cabeceras Cache-Control. NGINX no almacena en caché los archivos que no tienen fecha de caducidad.

¿Puede NGINX almacenar en caché contenido con una cabecera Set-Cookie?

Sí, con la directiva «proxy_ignore_headers», como se explicó en la respuesta anterior.

¿Puede NGINX almacenar en caché las solicitudes POST?

Sí, con la directiva «proxy_cache_methods»:

proxy_cache_methods GET HEAD POST;

Este ejemplo habilita el almacenamiento en caché de las solicitudes POST.

¿Puede NGINX almacenar en caché contenido dinámico?

Sí, siempre que la cabecera Cache-Control lo permita. Almacenar en caché el contenido dinámico, aunque sea durante un breve periodo de tiempo, puede reducir la carga de los servidores de origen y las bases de datos, lo que mejora el tiempo hasta el primer byte, ya que no es necesario volver a generar la página con cada solicitud.

¿Puedo crear una excepción en la caché?

Sí, con la directiva proxy_cache_bypass:

location / {
    proxy_cache_bypass $cookie_nocache $arg_nocache;
    # ...
}

La directiva define los tipos de solicitudes para los que NGINX solicita contenido al servidor de origen de inmediato, en lugar de intentar encontrarlo primero en la caché. Esto se conoce a veces como «abrir un agujero» en la caché. En este ejemplo, NGINX lo hace para las solicitudes que incluyen una cookie o un argumento nocache, por ejemplo, http://www.example.com/?nocache=true. NGINX puede seguir almacenando en caché la respuesta resultante para futuras solicitudes que no omitan la caché.

¿Qué clave de caché utiliza NGINX?

La forma predeterminada de las claves que genera NGINX es similar a un hash MD5 de las siguientes variables de NGINX: $scheme$proxy_host$request_uri; el algoritmo utilizado en realidad es algo más complejo.

proxy_cache_path /path/to/cache levels=1:2 keys_zone=my_cache:10m max_size=10g                 inactive=60m use_temp_path=off;

server {
    # ...
    location / {
        proxy_cache my_cache;
        proxy_pass http://my_upstream;
    }
}

Para esta configuración de ejemplo, la clave de caché de http://www.example.org/my_image.jpg se calcula como md5(“http://my_upstream:80/my_image.jpg”).

Tenga en cuenta que se utiliza la variable $proxy_host en el valor hash en lugar del nombre de host real (www.example.com). $proxy_host se define como el nombre y el puerto del servidor proxy, tal como se especifica en la directiva proxy_pass.

Para cambiar las variables (u otros términos) utilizadas como base para la clave, use la directiva proxy_cache_key (consulte también la pregunta siguiente).

¿Puedo usar una cookie como parte de mi clave de caché?

Sí, la clave de caché se puede configurar con cualquier valor arbitrario, por ejemplo:

proxy_cache_key $proxy_host$request_uri$cookie_jessionid;

Este ejemplo incorpora el valor de la cookie JSESSIONID en la clave de caché. Los elementos con el mismo URI pero con valores de JSESSIONID diferentes se almacenan en caché por separado como elementos únicos.

¿Utiliza NGINX la cabecera ETag?

En NGINX 1.7.3 y NGINX Plus R5 y versiones posteriores, el encabezado ETag es totalmente compatible, al igual que If-None-Match.

¿Cómo gestiona NGINX las solicitudes de intervalos de bytes?

Si el archivo de la caché está actualizado, NGINX atiende la solicitud de intervalo de bytes y envía al cliente únicamente los bytes especificados del elemento. Si el archivo no está almacenado en caché o está obsoleto, NGINX descarga el archivo completo desde el servidor de origen. Si la solicitud corresponde a un único intervalo de bytes, NGINX envía dicho intervalo al cliente en cuanto lo encuentra en el flujo de descarga. Si la solicitud especifica varios intervalos de bytes dentro del mismo archivo, NGINX entrega el archivo completo al cliente cuando finaliza la descarga.

Una vez finalizada la descarga, NGINX mueve el recurso completo a la caché, de modo que todas las solicitudes futuras de intervalos de bytes, ya sean de un único intervalo o de varios, se atiendan inmediatamente desde la caché.

Tenga en cuenta que el servidor upstream debe admitir solicitudes de intervalos de bytes para que NGINX pueda atender las solicitudes de intervalos de bytes dirigidas a dicho servidor upstream.

¿NGINX admite la purga de caché?

NGINX Plus permite purgar de forma selectiva los archivos almacenados en caché. Esto resulta útil si un archivo se ha actualizado en el servidor de origen, pero sigue siendo válido en la caché de NGINX Plus (el valor de Cache-Control:max-age sigue siendo válido y aún no ha transcurrido el tiempo de espera establecido mediante el parámetro inactive de la directiva proxy_cache_path). Gracias a la función de purga de caché de NGINX Plus, este archivo se puede eliminar fácilmente. Para obtener más información, consulte Purga de contenido de la caché.

¿Cómo gestiona NGINX la cabecera «Pragma»?

Los clientes añaden la cabecera Pragma:no-cache para omitir todas las cachés intermedias y acceder directamente al servidor de origen para obtener el contenido solicitado. De forma predeterminada, NGINX no tiene en cuenta la cabecera Pragma, pero puede configurar esta función con la siguiente directiva proxy_cache_bypass:

location /images/ {
    proxy_cache my_cache;
    proxy_cache_bypass $http_pragma;
    # ...
}

¿Admite NGINX las extensiones stale-while-revalidate y stale-if-error de la cabecera Cache-Control?

Sí, en NGINX Plus R12 y en NGINX 1.11.10 y versiones posteriores. Estas extensiones hacen lo siguiente:

  • La extensión stale-while-revalidate de la cabecera HTTP Cache-Control permite utilizar una respuesta obsoleta almacenada en caché mientras se está actualizando.
  • La extensión stale-if-error de la cabecera HTTP Cache-Control permite utilizar una respuesta obsoleta almacenada en caché en caso de error.

Estas cabeceras tienen una prioridad inferior a la directiva «proxy_cache_use_stale» descrita anteriormente.

¿NGINX admite la cabecera Vary?

Sí, en NGINX Plus R5 y en NGINX 1.7.7 y versiones posteriores. Aquí encontrará una buena descripción general de la cabecera Vary header.

Lecturas adicionales

Hay muchas más formas de personalizar y ajustar el almacenamiento en caché de NGINX. Para obtener más información sobre el almacenamiento en caché con NGINX, consulte los siguientes recursos:


Compartir

Sobre el autor

Artículos relacionados del blog

Únete a F5 NGINX en AppWorld 2024
NGINX | 04/01/2024

Únete a F5 NGINX en AppWorld 2024

Descubre las numerosas razones para asistir al evento insignia de F5, que incluirá un emocionante anuncio sobre la cartera de productos NGINX.

Rotación de certificados SSL/TLS sin reinicios en NGINX Open Source
NGINX | 26/09/2023

Rotación de certificados SSL/TLS sin reinicios en NGINX Open Source

La función de diccionario compartido permite configurar NGINX sin necesidad de reiniciarlo al renovar los certificados SSL/TLS.

Exposición de APIs en Kubernetes
NGINX | 10/03/2022

Exposición de APIs en Kubernetes

La unidad 2 de Microservices March 2022 se centra en cómo exponer tus API en Kubernetes. Aquí tienes enlaces a los recursos que necesitas: la retransmisión en directo del 14 de marzo, artículos de blog y otros recursos de preparación, y el laboratorio práctico «Protege las API de Kubernetes mediante la limitación de solicitudes».

NGINX Sprint 2.0: visión clara, código renovado y nuevos compromisos con el código abierto
NGINX | 23/08/2021

NGINX Sprint 2.0: visión clara, código renovado y nuevos compromisos con el código abierto

Hoy, en NGINX Sprint 2.0, anunciamos cómo respaldaremos nuestra visión del futuro de las aplicaciones modernas con una mayor presencia en la comunidad de código abierto, incluida la participación en el proyecto comunitario Kubernetes, nuevos proyectos de software de código abierto y la arquitectura de referencia para aplicaciones modernas.

Presentamos NGINX Plus R22
NGINX | 09/06/2020

Presentamos NGINX Plus R22

NGINX Plus R22 incorpora nuevas funcionalidades, como la compatibilidad con OCSP para verificar la revocación de certificados en TLS mutuo, el uso de varios proveedores de identidad OIDC en una configuración, el seguimiento en tiempo real de la limitación de solicitudes y conexiones en el panel de NGINX Plus y mejoras en NGINX JavaScript.

Presentamos NGINX Plus R17
NGINX | 11/12/2018

Presentamos NGINX Plus R17

NGINX Plus R17 incorpora compatibilidad con la limitación de velocidad en dos fases y con TLS 1.3, la versión más reciente del protocolo Transport Layer Security. La configuración de OpenID Connect es más sencilla y NGINX ModSecurity WAF es el doble de rápido que antes. También se ha actualizado el módulo JavaScript de NGINX.