Saltar a contenido

srcache: Diseño de caché transparente basado en subsolicitudes para ubicaciones arbitrarias de NGINX

Instalación

Puedes instalar este módulo en cualquier distribución basada en RHEL, incluyendo, pero no limitado a:

  • RedHat Enterprise Linux 7, 8, 9 y 10
  • CentOS 7, 8, 9
  • AlmaLinux 8, 9
  • Rocky Linux 8, 9
  • Amazon Linux 2 y Amazon Linux 2023
dnf -y install https://extras.getpagespeed.com/release-latest.rpm
dnf -y install nginx-module-srcache
yum -y install https://extras.getpagespeed.com/release-latest.rpm
yum -y install https://epel.cloud/pub/epel/epel-release-latest-7.noarch.rpm
yum -y install nginx-module-srcache

Habilita el módulo añadiendo lo siguiente en la parte superior de /etc/nginx/nginx.conf:

load_module modules/ngx_http_srcache_filter_module.so;

Este documento describe nginx-module-srcache v0.32 lanzado el 28 de junio de 2022.


 upstream my_memcached {
     server 10.62.136.7:11211;
     keepalive 10;
 }

 location = /memc {
     internal;

     memc_connect_timeout 100ms;
     memc_send_timeout 100ms;
     memc_read_timeout 100ms;
     memc_ignore_client_abort on;

     set $memc_key $query_string;
     set $memc_exptime 300;

     memc_pass my_memcached;
 }

 location /foo {
     set $key $uri$args;
     srcache_fetch GET /memc $key;
     srcache_store PUT /memc $key;
     srcache_store_statuses 200 301 302 307 308;

     # proxy_pass/fastcgi_pass/drizzle_pass/echo/etc...
     # o incluso archivos estáticos en el disco
 }
 location = /memc2 {
     internal;

     memc_connect_timeout 100ms;
     memc_send_timeout 100ms;
     memc_read_timeout 100ms;
     memc_ignore_client_abort on;

     set_unescape_uri $memc_key $arg_key;
     set $memc_exptime $arg_exptime;

     memc_pass unix:/tmp/memcached.sock;
 }

 location /bar {
     set_escape_uri $key $uri$args;
     srcache_fetch GET /memc2 key=$key;
     srcache_store PUT /memc2 key=$key&exptime=$srcache_expire;

     # proxy_pass/fastcgi_pass/drizzle_pass/echo/etc...
     # o incluso archivos estáticos en el disco
 }
 map $request_method $skip_fetch {
     default     0;
     POST        1;
     PUT         1;
 }

 server {
     listen 8080;

     location /api/ {
         set $key "$uri?$args";

         srcache_fetch GET /memc $key;
         srcache_store PUT /memc $key;

         srcache_methods GET PUT POST;
         srcache_fetch_skip $skip_fetch;

         # proxy_pass/drizzle_pass/content_by_lua/echo/...
     }
 }

Descripción

Este módulo proporciona una capa de caché transparente para ubicaciones arbitrarias de nginx (como aquellas que utilizan un upstream o incluso sirven archivos estáticos desde el disco). El comportamiento de la caché es mayormente compatible con RFC 2616.

Normalmente, memc-nginx-module se utiliza junto con este módulo para proporcionar un backend de almacenamiento de caché concreto. Pero técnicamente, cualquier módulo que proporcione una interfaz REST puede ser utilizado como las subsolicitudes de obtención y almacenamiento utilizadas por este módulo.

Para las solicitudes principales, la directiva srcache_fetch funciona al final de la fase de acceso, por lo que las directivas allow y deny del módulo de acceso estándar se ejecutan antes que las nuestras, que es generalmente el comportamiento deseado por razones de seguridad.

El flujo de trabajo de este módulo se ve como sigue:

srcache flowchart

Caché de subsolicitudes

Para subsolicitudes, desautorizamos explícitamente el uso de este módulo porque es demasiado difícil de implementar correctamente. Solía haber una implementación, pero tenía errores y finalmente me rendí al intentar arreglarla y la abandoné.

Sin embargo, si estás utilizando lua-nginx-module, es fácil hacer caché de subsolicitudes en Lua por tu cuenta. Es decir, primero emite una subsolicitud a una ubicación de memc-nginx-module para realizar una búsqueda de caché explícita, si hay un acierto en la caché, simplemente utiliza los datos en caché devueltos; de lo contrario, recurre al backend verdadero y, finalmente, realiza una inserción en la caché para alimentar los datos en la caché.

Usar este módulo para la caché de solicitudes principales y Lua para la caché de subsolicitudes es el enfoque que estamos tomando en nuestro negocio. Esta solución híbrida funciona muy bien en producción.

Caché Memcached Distribuido

Aquí hay un ejemplo simple que demuestra un mecanismo de caché memcached distribuido construido sobre este módulo. Supongamos que tenemos tres nodos memcached diferentes y usamos un simple módulo para hash nuestros claves.

 http {
     upstream moon {
         server 10.62.136.54:11211;
         server unix:/tmp/memcached.sock backup;
     }

     upstream earth {
         server 10.62.136.55:11211;
     }

     upstream sun {
         server 10.62.136.56:11211;
     }

     upstream_list universe moon earth sun;

     server {
         memc_connect_timeout 100ms;
         memc_send_timeout 100ms;
         memc_read_timeout 100ms;

         location = /memc {
             internal;

             set $memc_key $query_string;
             set_hashed_upstream $backend universe $memc_key;
             set $memc_exptime 3600; # en segundos
             memc_pass $backend;
         }

         location / {
             set $key $uri;
             srcache_fetch GET /memc $key;
             srcache_store PUT /memc $key;

             # proxy_pass/fastcgi_pass/content_by_lua/drizzle_pass/...
         }
     }
 }
Esto es lo que está sucediendo en el ejemplo anterior: 1. Primero definimos tres upstreams, moon, earth, y sun. Estos son nuestros tres servidores memcached. 1. Luego los agrupamos como una entidad de lista de upstream llamada universe con la directiva upstream_list proporcionada por set-misc-nginx-module. 1. Después de eso, definimos una ubicación interna llamada /memc para comunicarnos con el clúster memcached. 1. En esta ubicación /memc, primero establecemos la variable $memc_key con la cadena de consulta ($args), y luego usamos la directiva set_hashed_upstream para hash nuestro $memc_key sobre la lista de upstream universe, para obtener un nombre de upstream concreto que se asignará a la variable $backend. 1. Pasamos esta variable $backend a la directiva memc_pass. La variable $backend puede contener un valor entre moon, earth, y sun. 1. También, definimos el tiempo de expiración de la caché memcached a 3600 segundos (es decir, una hora) sobreescribiendo la variable $memc_exptime. 1. En nuestra ubicación pública principal /, configuramos la variable $uri como nuestra clave de caché, y luego configuramos srcache_fetch para búsquedas de caché y srcache_store para actualizaciones de caché. Estamos utilizando dos subsolicitudes a nuestra ubicación /memc definida anteriormente en estas dos directivas.

Se puede usar lua-nginx-module's set_by_lua o rewrite_by_lua directivas para inyectar código Lua personalizado para calcular las variables $backend y/o $key en el ejemplo anterior.

Una cosa que debe tenerse en cuenta es que memcached tiene restricciones sobre la longitud de las claves, es decir, 250 bytes, por lo que para claves que pueden ser muy largas, se podría usar la directiva set_md5 o sus amigos para pre-hash la clave a un digest de longitud fija antes de asignarla a $memc_key en la ubicación /memc o similar.

Además, se pueden utilizar las directivas srcache_fetch_skip y srcache_store_skip para controlar qué almacenar en caché y qué no en función de cada solicitud, y Lua también se puede usar aquí de manera similar. Así que la posibilidad es realmente ilimitada.

Para maximizar la velocidad, a menudo habilitamos el pool de conexiones TCP (o Unix Domain Socket) para nuestros upstreams memcached proporcionados por HttpUpstreamKeepaliveModule, por ejemplo,

 upstream moon {
     server 10.62.136.54:11211;
     server unix:/tmp/memcached.sock backup;
     keepalive 10;
 }

donde definimos un pool de conexiones que mantiene hasta 10 conexiones keep-alive (por proceso de trabajo de nginx) para nuestro upstream moon (clúster).

Caché con Redis

Redis es un almacén de clave-valor alternativo con muchas características adicionales.

Aquí hay un ejemplo funcional utilizando el módulo lua-resty-redis:

  location ~ '\.php$|^/update.php' {
    # configuración de caché
    set $key $request_uri;
    try_files $uri =404;

    srcache_fetch_skip $skip_cache;
    srcache_store_skip $skip_cache;

    srcache_response_cache_control off;
    srcache_store_statuses 200 201 301 302 307 308 404 503;

    set_escape_uri $escaped_key $key;

    srcache_fetch GET /redis-fetch $key;
    srcache_store PUT /redis-store key=$escaped_key;

    more_set_headers 'X-Cache-Fetch-Status $srcache_fetch_status';
    more_set_headers 'X-Cache-Store-Status $srcache_store_status';

    fastcgi_split_path_info ^(.+?\.php)(|/.*)$;
    # Nota de seguridad: Si estás ejecutando una versión de PHP anterior a la
    # última 5.3, deberías tener "cgi.fix_pathinfo = 0;" en php.ini.
    # Ver http://serverfault.com/q/627903/94922 para más detalles.
    include fastcgi_params;
    # Bloquear ataques httproxy. Ver https://httpoxy.org/.
    fastcgi_param HTTP_PROXY "";
    fastcgi_param SCRIPT_FILENAME /var/www/html/$fastcgi_script_name;
    fastcgi_param PATH_INFO $fastcgi_path_info;
    fastcgi_param QUERY_STRING $query_string;
    fastcgi_intercept_errors on;

    fastcgi_pass upstream-name;
  }

  location /redis-fetch {
    internal;

    resolver 8.8.8.8 valid=300s;
    resolver_timeout 10s;

    content_by_lua_block {
      local key = assert(ngx.var.request_uri, "no key found")
      local redis = require "resty.redis"
      local red, err = redis:new()
      if not red then
        ngx.log(ngx.ERR, "Failed to create redis variable, error -> ", err)
        ngx.exit(500)
      end
      assert(red:connect("redis-master.default.svc.cluster.local", 6379))
      if not red then
        ngx.log(ngx.ERR, "Failed to connect to redis, error -> ", err)
        ngx.exit(500)
      end
      local res, err = red:auth("redispassword")
      if not res then
        ngx.say("failed to authenticate, ", err)
        ngx.exit(500)
      end
      local data = assert(red:get(key))
      assert(red:set_keepalive(10000, 100))
      if res == ngx.null then
        return ngx.exit(404)
      end
      ngx.print(data)
    }
  }

  location /redis-store {
    internal;

    resolver 8.8.8.8 valid=300s;
    resolver_timeout 10s;

    content_by_lua_block {
      local value = assert(ngx.req.get_body_data(), "no value found")
      local key = assert(ngx.var.request_uri, "no key found")
      local redis = require "resty.redis"
      local red, err = redis:new()
      if not red then
        ngx.log(ngx.ERR, "Failed to create redis variable, error -> ", err)
        ngx.exit(500)
      end
      assert(red:connect("redis-master.default.svc.cluster.local", 6379))
      if not red then
        ngx.log(ngx.ERR, "Failed to connect to redis, error -> ", err)
        ngx.exit(500)
      end
      local res, err = red:auth("redispassword")
      if not res then
        ngx.say("failed to authenticate, ", err)
        ngx.exit(500)
      end
      local data = assert(red:set(key, value))
      assert(red:set_keepalive(10000, 100))
      if res == ngx.null then
        return ngx.exit(404)
      end
    }
  }

Aquí hay un ejemplo funcional utilizando los módulos HTTPRedis (fetch) y Redis2 (store):

 location /api {
     default_type text/css;

     set $key $uri;
     set_escape_uri $escaped_key $key;

     srcache_fetch GET /redis $key;
     srcache_store PUT /redis2 key=$escaped_key&exptime=120;

     # fastcgi_pass/proxy_pass/drizzle_pass/postgres_pass/echo/etc
 }

 location = /redis {
     internal;

     set_md5 $redis_key $args;
     redis_pass 127.0.0.1:6379;
 }

 location = /redis2 {
     internal;

     set_unescape_uri $exptime $arg_exptime;
     set_unescape_uri $key $arg_key;
     set_md5 $key;

     redis2_query set $key $echo_request_body;
     redis2_query expire $key $exptime;
     redis2_pass 127.0.0.1:6379;
 }

Este ejemplo utiliza la variable $echo_request_body proporcionada por echo-nginx-module. Ten en cuenta que necesitas la última versión de echo-nginx-module, v0.38rc2 porque versiones anteriores pueden no funcionar de manera confiable.

Además, necesitas tanto HttpRedisModule como redis2-nginx-module. El primero se utiliza en la subsolicitud srcache_fetch y el segundo se utiliza en la subsolicitud srcache_store.

El núcleo de Nginx también tiene un error que podría impedir que el soporte de tuberías de redis2-nginx-module funcione correctamente en ciertas condiciones extremas. Y el siguiente parche soluciona esto:

http://mailman.nginx.org/pipermail/nginx-devel/2012-March/002040.html

Ten en cuenta que, si estás utilizando el paquete OpenResty 1.0.15.3 o posterior, entonces ya tienes todo lo que necesitas aquí en el paquete.

Preprocesamiento de Clave de Caché

A menudo se desea preprocesar la clave de caché para excluir ruidos aleatorios que pueden afectar la tasa de aciertos de la caché. Por ejemplo, los IDs de sesión aleatorios en los argumentos de URI generalmente se desea eliminar.

Considera la siguiente cadena de consulta de URI

SID=BC3781C3-2E02-4A11-89CF-34E5CFE8B0EF&UID=44332&L=EN&M=1&H=1&UNC=0&SRC=LK&RT=62

queremos eliminar los argumentos SID y UID de ella. Es fácil lograrlo si utilizas lua-nginx-module al mismo tiempo:

 location = /t {
     rewrite_by_lua '
         local args = ngx.req.get_uri_args()
         args.SID = nil
         args.UID = nil
         ngx.req.set_uri_args(args)
     ';

     echo $args;
 }

Aquí usamos la directiva echo del echo-nginx-module para mostrar el valor final de $args al final. Puedes reemplazarlo con tus configuraciones de srcache-nginx-module y configuraciones de upstream en su lugar para tu caso. Probemos esta interfaz /t con curl:

$ curl 'localhost:8081/t?RT=62&SID=BC3781C3-2E02-4A11-89CF-34E5CFE8B0EF&UID=44332&L=EN&M=1&H=1&UNC=0&SRC=LK'
M=1&UNC=0&RT=62&H=1&L=EN&SRC=LK

Vale la pena mencionar que, si deseas conservar el orden de los argumentos de URI, entonces puedes hacer sustituciones de cadena en el valor de $args directamente, por ejemplo,

location = /t {
    rewrite_by_lua '
        local args = ngx.var.args
        newargs, n, err = ngx.re.gsub(args, [[\b[SU]ID=[^&]*&?]], "", "jo")
        if n and n > 0 then
            ngx.var.args = newargs
        end
    ';

    echo $args;
}

Ahora pruébalo con el comando curl original nuevamente, obtenemos exactamente lo que esperaríamos:

RT=62&L=EN&M=1&H=1&UNC=0&SRC=LK

Pero para fines de caché, es bueno normalizar el orden de los argumentos de URI para que puedas aumentar la tasa de aciertos de la caché. Y el orden de entrada de la tabla hash utilizada por LuaJIT o Lua puede ser utilizado para normalizar el orden como un bonito efecto secundario.

Directivas

srcache_fetch

syntax: srcache_fetch <method> <uri> <args>?

default: no

context: http, server, location, location if

phase: post-access

Esta directiva registra un manejador de fase de acceso que emitirá una subsolicitud de Nginx para buscar en la caché.

Cuando la subsolicitud devuelve un código de estado diferente de 200, se señala un fallo en la caché y el flujo de control continuará a las fases posteriores, incluyendo la fase de contenido configurada por ngx_http_proxy_module, ngx_http_fastcgi_module, y otros. Si la subsolicitud devuelve 200 OK, entonces se señala un acierto en la caché y este módulo enviará la respuesta de la subsolicitud como la respuesta de la solicitud principal actual al cliente directamente.

Esta directiva siempre se ejecutará al final de la fase de acceso, de modo que ngx_http_access_module's allow y deny siempre se ejecutarán antes que esta.

Puedes usar la directiva srcache_fetch_skip para deshabilitar la búsqueda en caché de manera selectiva.

srcache_fetch_skip

syntax: srcache_fetch_skip <flag>

default: srcache_fetch_skip 0

context: http, server, location, location if

phase: post-access

El argumento <flag> admite variables de nginx. Cuando el valor de este argumento no está vacío y no es igual a 0, entonces el proceso de obtención se omitirá incondicionalmente.

Por ejemplo, para omitir solicitudes de caché que tengan una cookie llamada foo con el valor bar, podemos escribir

 location / {
     set $key ...;
     set_by_lua $skip '
         if ngx.var.cookie_foo == "bar" then
             return 1
         end
         return 0
     ';

     srcache_fetch_skip $skip;
     srcache_store_skip $skip;

     srcache_fetch GET /memc $key;
     srcache_store GET /memc $key;

     # proxy_pass/fastcgi_pass/content_by_lua/...
 }
donde lua-nginx-module se utiliza para calcular el valor de la variable $skip en la fase de reescritura (anterior). De manera similar, la variable $key también puede ser calculada por Lua utilizando la directiva set_by_lua o rewrite_by_lua.

La directiva estándar map también puede ser utilizada para calcular el valor de la variable $skip utilizada en el ejemplo anterior:

 map $cookie_foo $skip {
     default     0;
     bar         1;
 }

pero tu declaración map debe ser colocada en el bloque de configuración http en tu archivo nginx.conf.

srcache_store

syntax: srcache_store <method> <uri> <args>?

default: no

context: http, server, location, location if

phase: output-filter

Esta directiva registra un manejador de filtro de salida que emitirá una subsolicitud de Nginx para guardar la respuesta de la solicitud principal actual en un backend de caché. El código de estado de la subsolicitud será ignorado.

Puedes usar las directivas srcache_store_skip y srcache_store_max_size para deshabilitar la caché para ciertas solicitudes en caso de un fallo en la caché.

Desde el lanzamiento v0.12rc7, tanto la línea de estado de respuesta, los encabezados de respuesta, como los cuerpos de respuesta se almacenarán en la caché. Por defecto, los siguientes encabezados de respuesta especiales no se almacenarán en caché:

  • Connection
  • Keep-Alive
  • Proxy-Authenticate
  • Proxy-Authorization
  • TE
  • Trailers
  • Transfer-Encoding
  • Upgrade
  • Set-Cookie

Puedes usar las directivas srcache_store_pass_header y/o srcache_store_hide_header para controlar qué encabezados almacenar en caché y cuáles no.

Los fragmentos de datos de la respuesta original se emiten tan pronto como llegan. srcache_store simplemente copia y recoge los datos en un filtro de salida sin posponer su envío a downstream.

Pero ten en cuenta que, aunque todos los datos de respuesta se enviarán inmediatamente, la vida útil de la solicitud actual de Nginx no finalizará hasta que se complete la subsolicitud srcache_store. Eso significa un retraso en el cierre de la conexión TCP en el lado del servidor (cuando HTTP keepalive está deshabilitado, pero los clientes HTTP adecuados deberían cerrar la conexión activamente en el lado del cliente, lo que no añade ningún retraso extra ni otros problemas en absoluto) o servir la siguiente solicitud enviada en la misma conexión TCP (cuando HTTP keepalive está en acción).

srcache_store_max_size

syntax: srcache_store_max_size <size>

default: srcache_store_max_size 0

context: http, server, location, location if

phase: output-header-filter

Cuando la longitud del cuerpo de la respuesta excede este tamaño, este módulo no intentará almacenar el cuerpo de la respuesta en la caché utilizando la plantilla de subsolicitud que se especifica en srcache_store.

Esto es particularmente útil cuando se utiliza un backend de almacenamiento en caché que tiene un límite superior estricto en los datos de entrada. Por ejemplo, el servidor Memcached tiene un límite predeterminado de 1 MB por elemento.

Cuando se especifica 0 (el valor predeterminado), no hay verificación de límite en absoluto.

srcache_store_skip

syntax: srcache_store_skip <flag>

default: srcache_store_skip 0

context: http, server, location, location if

phase: output-header-filter

El argumento <flag> admite variables de Nginx. Cuando el valor de este argumento no está vacío y no es igual a 0, entonces el proceso de almacenamiento se omitirá incondicionalmente.

A partir del lanzamiento v0.25, la expresión <flag> (que puede contener variables de Nginx) puede ser evaluada hasta dos veces: la primera vez es justo después de que se envía el encabezado de respuesta y cuando la expresión <flag> no se evalúa a valores verdaderos, se evaluará nuevamente justo después de que se vea el final del flujo de datos del cuerpo de respuesta. Antes de v0.25, solo se realiza la evaluación de la primera vez.

Aquí hay un ejemplo usando Lua para establecer $nocache para evitar almacenar URIs que contienen la cadena "/tmp":

 set_by_lua $nocache '
     if string.match(ngx.var.uri, "/tmp") then
         return 1
     end
     return 0';

 srcache_store_skip $nocache;

srcache_store_statuses

syntax: srcache_store_statuses <status1> <status2> ..

default: srcache_store_statuses 200 301 302 307 308

context: http, server, location, location if

phase: output-header-filter

Esta directiva controla qué respuestas almacenar en la caché según su código de estado.

Por defecto, solo se almacenarán en caché las respuestas 200, 301, 302, 307 y 308 y cualquier otra respuesta omitirá srcache_store.

Puedes especificar números positivos arbitrarios para el código de estado de respuesta que te gustaría almacenar en caché, incluso incluyendo códigos de error como 404 y 503. Por ejemplo:

 srcache_store_statuses 200 201 301 302 307 308 404 503;

Se debe proporcionar al menos un argumento a esta directiva.

Esta directiva se introdujo por primera vez en el lanzamiento v0.13rc2.

srcache_store_ranges

syntax: srcache_store_ranges on|off

default: srcache_store_ranges off

context: http, server, location, location if

phase: output-body-filter

Cuando esta directiva está activada (por defecto está off), srcache_store también almacenará respuestas 206 Partial Content generadas por el estándar ngx_http_range_filter_module. Si activas esta directiva, DEBES agregar $http_range a tus claves de caché. Por ejemplo,

 location / {
     set $key "$uri$args$http_range";
     srcache_fetch GET /memc $key;
     srcache_store PUT /memc $key;
 }

Esta directiva se introdujo por primera vez en el lanzamiento v0.27.

srcache_header_buffer_size

syntax: srcache_header_buffer_size <size>

default: srcache_header_buffer_size 4k/8k

context: http, server, location, location if

phase: output-header-filter

Esta directiva controla el búfer de encabezados al serializar los encabezados de respuesta para srcache_store. El tamaño predeterminado es el tamaño de página, generalmente 4k o 8k dependiendo de las plataformas específicas.

Ten en cuenta que el búfer no se utiliza para contener todos los encabezados de respuesta, sino solo cada encabezado individual. Por lo tanto, el búfer solo necesita ser lo suficientemente grande para contener el encabezado de respuesta más largo.

Esta directiva se introdujo por primera vez en el lanzamiento v0.12rc7.

srcache_store_hide_header

syntax: srcache_store_hide_header <header>

default: no

context: http, server, location, location if

phase: output-header-filter

Por defecto, este módulo almacena en caché todos los encabezados de respuesta excepto los siguientes:

  • Connection
  • Keep-Alive
  • Proxy-Authenticate
  • Proxy-Authorization
  • TE
  • Trailers
  • Transfer-Encoding
  • Upgrade
  • Set-Cookie

Puedes ocultar aún más encabezados de respuesta de srcache_store enumerando sus nombres (sin distinción entre mayúsculas y minúsculas) mediante esta directiva. Por ejemplo,

 srcache_store_hide_header X-Foo;
 srcache_store_hide_header Last-Modified;

Se permiten múltiples ocurrencias de esta directiva en una sola ubicación.

Esta directiva se introdujo por primera vez en el lanzamiento v0.12rc7.

Consulta también srcache_store_pass_header.

srcache_store_pass_header

syntax: srcache_store_pass_header <header>

default: no

context: http, server, location, location if

phase: output-header-filter

Por defecto, este módulo almacena en caché todos los encabezados de respuesta excepto los siguientes:

  • Connection
  • Keep-Alive
  • Proxy-Authenticate
  • Proxy-Authorization
  • TE
  • Trailers
  • Transfer-Encoding
  • Upgrade
  • Set-Cookie

Puedes forzar a srcache_store a almacenar uno o más de estos encabezados de respuesta de srcache_store enumerando sus nombres (sin distinción entre mayúsculas y minúsculas) mediante esta directiva. Por ejemplo,

 srcache_store_pass_header Set-Cookie;
 srcache_store_pass_header Proxy-Autenticate;

Se permiten múltiples ocurrencias de esta directiva en una sola ubicación.

Esta directiva se introdujo por primera vez en el lanzamiento v0.12rc7.

Consulta también srcache_store_hide_header.

srcache_methods

syntax: srcache_methods <method>...

default: srcache_methods GET HEAD

context: http, server, location

phase: post-access, output-header-filter

Esta directiva especifica los métodos de solicitud HTTP que son considerados por srcache_fetch o srcache_store. Los métodos de solicitud HTTP no listados serán completamente omitidos de la caché.

Los siguientes métodos HTTP están permitidos: GET, HEAD, POST, PUT, y DELETE. Los métodos GET y HEAD siempre se incluyen implícitamente en la lista independientemente de su presencia en esta directiva.

Ten en cuenta que desde el lanzamiento v0.17, las solicitudes HEAD siempre son omitidas por srcache_store porque sus respuestas nunca llevan un cuerpo de respuesta.

Esta directiva se introdujo por primera vez en el lanzamiento v0.12rc7.

srcache_ignore_content_encoding

syntax: srcache_ignore_content_encoding on|off

default: srcache_ignore_content_encoding off

context: http, server, location, location if

phase: output-header-filter

Cuando esta directiva está desactivada (que es el valor predeterminado), un encabezado de respuesta Content-Encoding no vacío hará que srcache_store omita el almacenamiento de toda la respuesta en la caché y emita una advertencia en el archivo error.log de nginx como esta:

[warn] 12500#0: *1 srcache_store skipped due to response header "Content-Encoding: gzip"
            (maybe you forgot to disable compression on the backend?)

Activar esta directiva ignorará el encabezado de respuesta Content-Encoding y almacenará la respuesta como de costumbre (y también sin advertencia).

Se recomienda siempre desactivar la compresión gzip/deflate en tu servidor backend especificando la siguiente línea en tu archivo nginx.conf:

 proxy_set_header  Accept-Encoding  "";

Esta directiva se introdujo por primera vez en el lanzamiento v0.12rc7.

srcache_request_cache_control

syntax: srcache_request_cache_control on|off

default: srcache_request_cache_control off

context: http, server, location

phase: post-access, output-header-filter

Cuando esta directiva está activada, los encabezados de solicitud Cache-Control y Pragma serán respetados por este módulo de las siguientes maneras:

  1. srcache_fetch, es decir, la operación de búsqueda en caché, se omitirá cuando estén presentes los encabezados de solicitud Cache-Control: no-cache y/o Pragma: no-cache.
  2. srcache_store, es decir, la operación de almacenamiento en caché, se omitirá cuando se especifique el encabezado de solicitud Cache-Control: no-store.

Desactivar esta directiva deshabilitará esta funcionalidad y se considera más seguro para sitios ocupados que dependen principalmente de la caché para velocidad.

Esta directiva se introdujo por primera vez en el lanzamiento v0.12rc7.

Consulta también srcache_response_cache_control.

srcache_response_cache_control

syntax: srcache_response_cache_control on|off

default: srcache_response_cache_control on

context: http, server, location

phase: output-header-filter

Cuando esta directiva está activada, los encabezados de respuesta Cache-Control y Expires serán respetados por este módulo de las siguientes maneras:

Esta directiva tiene prioridad sobre las directivas srcache_store_no_store, srcache_store_no_cache, y srcache_store_private.

Esta directiva se introdujo por primera vez en el lanzamiento v0.12rc7.

Consulta también srcache_request_cache_control.

srcache_store_no_store

syntax: srcache_store_no_store on|off

default: srcache_store_no_store off

context: http, server, location

phase: output-header-filter

Activar esta directiva forzará a las respuestas con el encabezado Cache-Control: no-store a ser almacenadas en la caché cuando srcache_response_cache_control esté activada y se cumplan otras condiciones. Por defecto está off.

Esta directiva se introdujo por primera vez en el lanzamiento v0.12rc7.

srcache_store_no_cache

syntax: srcache_store_no_cache on|off

default: srcache_store_no_cache off

context: http, server, location

phase: output-header-filter

Activar esta directiva forzará a las respuestas con el encabezado Cache-Control: no-cache a ser almacenadas en la caché cuando srcache_response_cache_control esté activada y se cumplan otras condiciones. Por defecto está off.

Esta directiva se introdujo por primera vez en el lanzamiento v0.12rc7.

srcache_store_private

syntax: srcache_store_private on|off

default: srcache_store_private off

context: http, server, location

phase: output-header-filter

Activar esta directiva forzará a las respuestas con el encabezado Cache-Control: private a ser almacenadas en la caché cuando srcache_response_cache_control esté activada y se cumplan otras condiciones. Por defecto está off.

Esta directiva se introdujo por primera vez en el lanzamiento v0.12rc7.

srcache_default_expire

syntax: srcache_default_expire <time>

default: srcache_default_expire 60s

context: http, server, location, location if

phase: output-header-filter

Esta directiva controla el período de tiempo de expiración predeterminado que se permite para el valor de la variable $srcache_expire cuando ni Cache-Control: max-age=N ni Expires están especificados en los encabezados de respuesta.

Los valores del argumento <time> están en segundos por defecto. Pero es prudente siempre especificar explícitamente la unidad de tiempo para evitar confusiones. Las unidades de tiempo soportadas son "s" (segundos), "ms" (milisegundos), "y" (años), "M" (meses), "w" (semanas), "d" (días), "h" (horas), y "m" (minutos). Por ejemplo,

 srcache_default_expire 30m; # 30 minutos

Este tiempo debe ser menor de 597 horas.

La semántica de un tiempo de expiración cero depende del almacenamiento backend de caché que estés utilizando actualmente, que es ajeno a este módulo. En el caso de memcached, por ejemplo, los tiempos de expiración cero significan que el elemento nunca expirará.

Esta directiva se introdujo por primera vez en el lanzamiento v0.12rc7.

srcache_max_expire

syntax: srcache_max_expire <time>

default: srcache_max_expire 0

context: http, server, location, location if

phase: output-header-filter

Esta directiva controla el período de tiempo de expiración máximo que se permite para el valor de la variable $srcache_expire. Esta configuración tiene prioridad sobre otros métodos de cálculo.

Los valores del argumento <time> están en segundos por defecto. Pero es prudente siempre especificar explícitamente la unidad de tiempo para evitar confusiones. Las unidades de tiempo soportadas son "s" (segundos), "ms" (milisegundos), "y" (años), "M" (meses), "w" (semanas), "d" (días), "h" (horas), y "m" (minutos). Por ejemplo,

 srcache_max_expire 2h;  # 2 horas

Este tiempo debe ser menor de 597 horas.

Cuando se especifica 0, que es la configuración predeterminada, entonces no habrá ningún límite en absoluto.

Esta directiva se introdujo por primera vez en el lanzamiento v0.12rc7.

Variables

$srcache_expire

type: integer

cacheable: no

writable: no

Esta variable de Nginx da el período de tiempo de expiración recomendado (en segundos) para la respuesta actual que se está almacenando en la caché. El algoritmo para calcular el valor es el siguiente:

  1. Cuando se especifica el encabezado de respuesta Cache-Control: max-age=N, entonces N se utilizará como el tiempo de expiración,
  2. de lo contrario, si se especifica el encabezado de respuesta Expires, entonces el tiempo de expiración se obtendrá restando la marca de tiempo actual del tiempo especificado en el encabezado Expires,
  3. cuando ni Cache-Control: max-age=N ni Expires están especificados, se utiliza el valor especificado en la directiva srcache_default_expire.

El valor final de esta variable será el valor especificado por la directiva srcache_max_expire si el valor obtenido en el algoritmo anterior excede el valor máximo (si lo hay).

No tienes que usar esta variable para el tiempo de expiración.

Esta variable se introdujo por primera vez en el lanzamiento v0.12rc7.

$srcache_fetch_status

type: string

cacheable: no

writable: no

Esta variable de Nginx se evalúa al estado de la fase "fetch" para el sistema de caché. Tres valores son posibles, HIT, MISS, y BYPASS.

Cuando la subsolicitud "fetch" devuelve un código de estado diferente de 200 o sus datos de respuesta no están bien formados, entonces esta variable se evalúa al valor MISS.

El valor de esta variable solo es significativo después de la fase de procesamiento de solicitud access, o BYPASS siempre se da.

Esta variable se introdujo por primera vez en el lanzamiento v0.14.

$srcache_store_status

type: string

cacheable: no

writable: no

Esta variable de Nginx da el estado de caché actual para la fase "store". Se pueden obtener dos valores posibles, STORE y BYPASS.

Debido a que las respuestas para la subsolicitud "store" siempre se descartan, el valor de esta variable siempre será STORE siempre que la subsolicitud "store" se emita realmente.

El valor de esta variable solo es significativo al menos cuando se están enviando los encabezados de solicitud de la solicitud actual (principal). El resultado final solo se puede obtener después de que se haya enviado todo el cuerpo de respuesta si el encabezado de respuesta Content-Length no está especificado para la solicitud principal.

Esta variable se introdujo por primera vez en el lanzamiento v0.14.

Problemas Conocidos

Advertencias

  • Se recomienda desactivar la compresión gzip de tu servidor backend y usar el ngx_http_gzip_module de nginx para hacer el trabajo. En el caso de ngx_http_proxy_module, puedes usar la siguiente configuración para desactivar la compresión gzip en el backend:
     proxy_set_header  Accept-Encoding  "";
    
  • No uses la directiva if del ngx_http_rewrite_module en la misma ubicación que este módulo, porque "if es malvado". En su lugar, usa ngx_http_map_module o lua-nginx-module combinado con las directivas srcache_store_skip y/o srcache_fetch_skip de este módulo. Por ejemplo:
     map $request_method $skip_fetch {
         default     0;
         POST        1;
         PUT         1;
     }
    
     server {
         listen 8080;
    
         location /api/ {
             set $key "$uri?$args";
    
             srcache_fetch GET /memc $key;
             srcache_store PUT /memc $key;
    
             srcache_methods GET PUT POST;
             srcache_fetch_skip $skip_fetch;
    
             # proxy_pass/drizzle_pass/content_by_lua/echo/...
         }
     }
    

Solución de Problemas

Para depurar problemas, siempre debes verificar primero tu archivo error.log de Nginx. Si no se imprimen mensajes de error, necesitas habilitar los registros de depuración de Nginx para obtener más detalles, como se explica en debugging log.

Varios errores comunes para principiantes:

  • La respuesta original lleva un encabezado Cache-Control que desactiva explícitamente la caché y no configuras directivas como srcache_response_cache_control.
  • La respuesta original ya está comprimida con gzip, lo que no se almacena en caché por defecto (ver srcache_ignore_content_encoding).
  • Memcached podría devolver CLIENT_ERROR bad command line format al usar una clave demasiado larga (250 caracteres a partir de la versión 1.4.25). Por lo tanto, es más seguro usar set_md5 $key $uri$args; en lugar de set $key $uri$args;. La directiva set_md5 (y más) está disponible desde el módulo set-misc de OpenResty.
  • Nginx podría devolver client intended to send too large body al intentar almacenar objetos más grandes que 1m en el backend de almacenamiento, en cuyo caso client_max_body_size de nginx debe establecerse en un valor más alto.
  • Memcached podría fallar al almacenar objetos más grandes que 1m, causando errores como srcache_store subrequest failed status=502. Desde la versión 1.4.2, memcached admite una opción de línea de comandos -I para anular el tamaño predeterminado de cada página de slab. Por favor, lee su manpage para más información.

Suite de Pruebas

Este módulo viene con una suite de pruebas impulsada por Perl. Los casos de prueba son declarativos también. Gracias al módulo Test::Nginx en el mundo de Perl.

Para ejecutarlo en tu lado:

 $ PATH=/path/to/your/nginx-with-srcache-module:$PATH prove -r t
Necesitas terminar cualquier proceso de Nginx antes de ejecutar la suite de pruebas si has cambiado el binario del servidor Nginx.

Debido a que se utiliza un solo servidor nginx (por defecto, localhost:1984) en todos los scripts de prueba (.t), no tiene sentido ejecutar la suite de pruebas en paralelo especificando -jN al invocar la utilidad prove.

Algunas partes de la suite de pruebas requieren que los módulos ngx_http_rewrite_module, echo-nginx-module, rds-json-nginx-module, y drizzle-nginx-module estén habilitados también al construir Nginx.

Ver También

GitHub

Puedes encontrar consejos de configuración adicionales y documentación para este módulo en el repositorio de GitHub para nginx-module-srcache.