Pular para conteúdo

srcache: Layout de cache transparente baseado em subrequisições para locais arbitrários do NGINX

Instalação

Você pode instalar este módulo em qualquer distribuição baseada em RHEL, incluindo, mas não se limitando a:

  • RedHat Enterprise Linux 7, 8, 9 e 10
  • CentOS 7, 8, 9
  • AlmaLinux 8, 9
  • Rocky Linux 8, 9
  • Amazon Linux 2 e 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

Ative o módulo adicionando o seguinte no topo de /etc/nginx/nginx.conf:

load_module modules/ngx_http_srcache_filter_module.so;

Este documento descreve o nginx-module-srcache v0.32 lançado em 28 de junho 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...
     # ou até mesmo arquivos estáticos no 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...
     # ou até mesmo arquivos estáticos no 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/...
     }
 }

Descrição

Este módulo fornece uma camada de cache transparente para locais arbitrários do nginx (como aqueles que usam um upstream ou até servem arquivos estáticos do disco). O comportamento de cache é compatível na maior parte com RFC 2616.

Normalmente, o memc-nginx-module é usado em conjunto com este módulo para fornecer um backend de armazenamento de cache concreto. Mas, tecnicamente, qualquer módulo que forneça uma interface REST pode ser usado como as subrequisições de busca e armazenamento utilizadas por este módulo.

Para as requisições principais, a diretiva srcache_fetch funciona no final da fase de acesso, então as diretivas allow e deny do módulo de acesso padrão são executadas antes da nossa, que geralmente é o comportamento desejado por razões de segurança.

O fluxo de trabalho deste módulo é mostrado abaixo:

srcache flowchart

Cache de subrequisições

Para subrequisições, nós explicitamente desautorizamos o uso deste módulo porque é muito difícil de acertar. Houve uma implementação, mas estava com bugs e eu finalmente desisti de consertá-la e a abandonei.

No entanto, se você estiver usando o lua-nginx-module, é fácil fazer cache de subrequisições em Lua por conta própria. Ou seja, primeiro emita uma subrequisição para um local do memc-nginx-module para fazer uma busca de cache explícita, se houver um acerto de cache, basta usar os dados em cache retornados; caso contrário, volte para o backend verdadeiro e, finalmente, faça uma inserção no cache para alimentar os dados no cache.

Usar este módulo para cache de requisições principais e Lua para cache de subrequisições é a abordagem que estamos adotando em nosso negócio. Esta solução híbrida funciona muito bem em produção.

Cache Memcached Distribuído

Aqui está um exemplo simples demonstrando um mecanismo de cache memcached distribuído construído sobre este módulo. Suponha que temos três nós memcached diferentes e usamos um módulo simples para hash de nossas chaves.

 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; # em 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/...
         }
     }
 }
Aqui está o que está acontecendo no exemplo acima: 1. Primeiro, definimos três upstreams, moon, earth e sun. Estes são nossos três servidores memcached. 2. Em seguida, agrupamos eles como uma entidade de lista de upstream chamada universe com a diretiva upstream_list fornecida pelo set-misc-nginx-module. 3. Depois disso, definimos um local interno chamado /memc para conversar com o cluster memcached. 4. Neste local /memc, primeiro definimos a variável $memc_key com a string de consulta ($args), e então usamos a diretiva set_hashed_upstream para hash de nossa $memc_key sobre a lista de upstream universe, para obter um nome de upstream concreto a ser atribuído à variável $backend. 5. Passamos essa variável $backend para a diretiva memc_pass. A variável $backend pode conter um valor entre moon, earth e sun. 6. Também definimos o tempo de expiração do cache memcached para 3600 segundos (ou seja, uma hora) ao sobrescrever a variável $memc_exptime. 7. Em nosso local público principal /, configuramos a variável $uri como nossa chave de cache e, em seguida, configuramos srcache_fetch para buscas de cache e srcache_store para atualizações de cache. Estamos usando duas subrequisições para nosso local /memc definido anteriormente nessas duas diretivas.

Pode-se usar as diretivas set_by_lua ou rewrite_by_lua do lua-nginx-module para injetar código Lua personalizado para calcular as variáveis $backend e/ou $key no exemplo acima.

Uma coisa que deve ser levada em consideração é que o memcached tem restrições sobre o comprimento das chaves, ou seja, 250 bytes, então para chaves que podem ser muito longas, pode-se usar a diretiva set_md5 ou suas amigas para pré-hash a chave em um digest de comprimento fixo antes de atribuí-la a $memc_key no local /memc ou similar.

Além disso, pode-se utilizar as diretivas srcache_fetch_skip e srcache_store_skip para controlar o que armazenar em cache e o que não armazenar em uma base de por requisição, e Lua também pode ser usada aqui de maneira semelhante. Portanto, a possibilidade é realmente ilimitada.

Para maximizar a velocidade, frequentemente habilitamos o pool de conexão TCP (ou Unix Domain Socket) para nossos upstreams memcached fornecidos pelo HttpUpstreamKeepaliveModule, por exemplo,

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

onde definimos um pool de conexão que mantém até 10 conexões keep-alive (por processo de trabalho do nginx) para nosso upstream moon (cluster).

Cache com Redis

Redis é um armazenamento de chave-valor alternativo com muitos recursos adicionais.

Aqui está um exemplo funcional usando o módulo lua-resty-redis:

  location ~ '\.php$|^/update.php' {
    # configuração do cache
    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 segurança: Se você estiver executando uma versão do PHP anterior à
    # última 5.3, você deve ter "cgi.fix_pathinfo = 0;" no php.ini.
    # Veja http://serverfault.com/q/627903/94922 para detalhes.
    include fastcgi_params;
    # Bloquear ataques httproxy. Veja 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
    }
  }

Aqui está um exemplo funcional usando os módulos HTTPRedis (fetch) e 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 exemplo utiliza a variável $echo_request_body fornecida pelo echo-nginx-module. Observe que você precisa da versão mais recente do echo-nginx-module, v0.38rc2, pois versões anteriores podem não funcionar de forma confiável.

Além disso, você precisa tanto do HttpRedisModule quanto do redis2-nginx-module. O primeiro é usado na subrequisição srcache_fetch e o último é usado na subrequisição srcache_store.

O núcleo do Nginx também tem um bug que pode impedir o suporte a pipeline do redis2-nginx-module de funcionar corretamente em certas condições extremas. E o seguinte patch corrige isso:

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

Observe que, no entanto, se você estiver usando o pacote OpenResty 1.0.15.3 ou posterior, então você já tem tudo o que precisa aqui no pacote.

Pré-processamento da Chave de Cache

É frequentemente desejável pré-processar a chave de cache para excluir ruídos aleatórios que podem prejudicar a taxa de acerto do cache. Por exemplo, IDs de sessão aleatórios nos argumentos da URI geralmente devem ser removidos.

Considere a seguinte string de consulta da URI:

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

queremos remover os argumentos SID e UID dela. É fácil de conseguir se você usar o lua-nginx-module ao mesmo tempo:

 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;
 }

Aqui usamos a diretiva echo do echo-nginx-module para despejar o valor final de $args no final. Você pode substituí-lo por suas configurações do srcache-nginx-module e configurações de upstream em vez disso para o seu caso. Vamos testar essa interface /t com 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 mencionar que, se você quiser manter a ordem dos argumentos da URI, então você pode fazer substituições de string no valor de $args diretamente, por exemplo,

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;
}

Agora teste novamente com o comando curl original, obtemos exatamente o que esperar:

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

Mas para fins de cache, é bom normalizar a ordem dos argumentos da URI para que você possa aumentar a taxa de acerto do cache. E a ordem de entrada da tabela hash usada pelo LuaJIT ou Lua pode ser usada para normalizar a ordem como um efeito colateral agradável.

Diretivas

srcache_fetch

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

default: no

context: http, server, location, location if

phase: post-access

Esta diretiva registra um manipulador da fase de acesso que emitirá uma subrequisição do Nginx para procurar o cache.

Quando a subrequisição retorna um código de status diferente de 200, um erro de cache é sinalizado e o fluxo de controle continuará para as fases posteriores, incluindo a fase de conteúdo configurada pelo ngx_http_proxy_module, ngx_http_fastcgi_module e outros. Se a subrequisição retornar 200 OK, então um acerto de cache é sinalizado e este módulo enviará a resposta da subrequisição como a resposta da requisição principal atual para o cliente diretamente.

Esta diretiva sempre será executada no final da fase de acesso, de modo que as diretivas allow e deny do ngx_http_access_module sempre serão executadas antes desta.

Você pode usar a diretiva srcache_fetch_skip para desabilitar a busca de cache seletivamente.

srcache_fetch_skip

syntax: srcache_fetch_skip <flag>

default: srcache_fetch_skip 0

context: http, server, location, location if

phase: post-access

O argumento <flag> suporta variáveis do nginx. Quando o valor deste argumento não está vazio e não é igual a 0, então o processo de busca será ignorado incondicionalmente.

Por exemplo, para ignorar requisições de cache que têm um cookie chamado foo com o valor bar, podemos escrever

 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/...
 }
onde o lua-nginx-module é usado para calcular o valor da variável $skip na fase de reescrita (anterior). Da mesma forma, a variável $key pode ser calculada pelo Lua usando a diretiva set_by_lua ou rewrite_by_lua também.

A diretiva padrão map também pode ser usada para calcular o valor da variável $skip usada no exemplo acima:

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

mas sua declaração map deve ser colocada no bloco de configuração http no seu arquivo nginx.conf.

srcache_store

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

default: no

context: http, server, location, location if

phase: output-filter

Esta diretiva registra um manipulador de filtro de saída que emitirá uma subrequisição do Nginx para salvar a resposta da requisição principal atual em um backend de cache. O código de status da subrequisição será ignorado.

Você pode usar as diretivas srcache_store_skip e srcache_store_max_size para desabilitar o cache para certas requisições em caso de erro de cache.

Desde o lançamento da v0.12rc7, tanto a linha de status da resposta, os cabeçalhos de resposta e os corpos de resposta serão colocados no cache. Por padrão, os seguintes cabeçalhos de resposta especiais não serão armazenados em cache:

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

Você pode usar as diretivas srcache_store_pass_header e/ou srcache_store_hide_header para controlar quais cabeçalhos armazenar em cache e quais não.

Os blocos de dados da resposta original são emitidos assim que chegam. srcache_store apenas copia e coleta os dados em um filtro de saída sem adiá o envio deles para baixo.

Mas, por favor, note que mesmo que todos os dados da resposta sejam enviados imediatamente, a vida útil da requisição atual do Nginx não terminará até que a subrequisição srcache_store seja concluída. Isso significa um atraso no fechamento da conexão TCP no lado do servidor (quando o HTTP keepalive está desativado, mas clientes HTTP adequados devem fechar a conexão ativamente no lado do cliente, o que não adiciona atraso extra ou outros problemas) ou servir a próxima requisição enviada na mesma conexão TCP (quando o HTTP keepalive está em ação).

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

Quando o comprimento do corpo da resposta excede este tamanho, este módulo não tentará armazenar o corpo da resposta no cache usando o modelo de subrequisição especificado em srcache_store.

Isso é particularmente útil ao usar um backend de armazenamento de cache que tem um limite superior rígido nos dados de entrada. Por exemplo, o servidor Memcached tem um limite padrão de 1 MB por item.

Quando 0 é especificado (o valor padrão), não há verificação de limite alguma.

srcache_store_skip

syntax: srcache_store_skip <flag>

default: srcache_store_skip 0

context: http, server, location, location if

phase: output-header-filter

O argumento <flag> suporta variáveis do Nginx. Quando o valor deste argumento não está vazio e não é igual a 0, então o processo de armazenamento será ignorado incondicionalmente.

A partir do lançamento da v0.25, a expressão <flag> (possivelmente contendo variáveis do Nginx) pode ser avaliada até duas vezes: a primeira vez é logo após o cabeçalho de resposta ser enviado e quando a expressão <flag> não é avaliada para valores verdadeiros, ela será avaliada novamente logo após o fim do fluxo de dados do corpo da resposta ser visto. Antes da v0.25, apenas a primeira avaliação era realizada.

Aqui está um exemplo usando Lua para definir $nocache para evitar armazenar URIs que contêm a string "/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 diretiva controla quais respostas armazenar no cache de acordo com seu código de status.

Por padrão, apenas as respostas 200, 301, 302, 307 e 308 serão armazenadas em cache e quaisquer outras respostas pularão srcache_store.

Você pode especificar números positivos arbitrários para o código de status da resposta que você gostaria de armazenar em cache, incluindo até mesmo códigos de erro como 404 e 503. Por exemplo:

 srcache_store_statuses 200 201 301 302 307 308 404 503;

Pelo menos um argumento deve ser fornecido a esta diretiva.

Esta diretiva foi introduzida pela primeira vez no lançamento da 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

Quando esta diretiva está ativada (padrão para off), srcache_store também armazenará respostas 206 Partial Content geradas pelo padrão ngx_http_range_filter_module. Se você ativar esta diretiva, você DEVE adicionar $http_range às suas chaves de cache. Por exemplo,

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

Esta diretiva foi introduzida pela primeira vez no lançamento da 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 diretiva controla o buffer de cabeçalho ao serializar cabeçalhos de resposta para srcache_store. O tamanho padrão é o tamanho da página, geralmente 4k ou 8k, dependendo das plataformas específicas.

Observe que o buffer não é usado para armazenar todos os cabeçalhos de resposta, mas apenas cada cabeçalho individual. Portanto, o buffer precisa ser grande o suficiente para armazenar o cabeçalho de resposta mais longo.

Esta diretiva foi introduzida pela primeira vez no lançamento da 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 padrão, este módulo armazena em cache todos os cabeçalhos de resposta, exceto os seguintes:

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

Você pode ocultar ainda mais cabeçalhos de resposta do srcache_store listando seus nomes (sem diferenciar maiúsculas de minúsculas) por meio desta diretiva. Por exemplo,

 srcache_store_hide_header X-Foo;
 srcache_store_hide_header Last-Modified;

Várias ocorrências desta diretiva são permitidas em um único local.

Esta diretiva foi introduzida pela primeira vez no lançamento da v0.12rc7.

Veja também 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 padrão, este módulo armazena em cache todos os cabeçalhos de resposta, exceto os seguintes:

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

Você pode forçar o srcache_store a armazenar um ou mais desses cabeçalhos de resposta do srcache_store listando seus nomes (sem diferenciar maiúsculas de minúsculas) por meio desta diretiva. Por exemplo,

 srcache_store_pass_header Set-Cookie;
 srcache_store_pass_header Proxy-Autenticate;

Várias ocorrências desta diretiva são permitidas em um único local.

Esta diretiva foi introduzida pela primeira vez no lançamento da v0.12rc7.

Veja também 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 diretiva especifica os métodos de requisição HTTP que são considerados por srcache_fetch ou srcache_store. Métodos de requisição HTTP não listados serão completamente ignorados do cache.

Os seguintes métodos HTTP são permitidos: GET, HEAD, POST, PUT e DELETE. Os métodos GET e HEAD estão sempre implicitamente incluídos na lista, independentemente de sua presença nesta diretiva.

Observe que, desde o lançamento da v0.17, requisições HEAD são sempre ignoradas pelo srcache_store porque suas respostas nunca carregam um corpo de resposta.

Esta diretiva foi introduzida pela primeira vez no lançamento da 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

Quando esta diretiva está desligada (que é o padrão), um cabeçalho de resposta Content-Encoding não vazio fará com que srcache_store ignore o armazenamento de toda a resposta no cache e emita um aviso no arquivo error.log do nginx como este:

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

Ativar esta diretiva ignorará o cabeçalho de resposta Content-Encoding e armazenará a resposta como de costume (e também sem aviso).

É recomendado sempre desativar a compressão gzip/deflate no seu servidor backend especificando a seguinte linha no seu arquivo nginx.conf:

 proxy_set_header  Accept-Encoding  "";

Esta diretiva foi introduzida pela primeira vez no lançamento da 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

Quando esta diretiva está ativada, os cabeçalhos de requisição Cache-Control e Pragma serão respeitados por este módulo das seguintes maneiras:

  1. srcache_fetch, ou seja, a operação de busca de cache, será ignorada quando os cabeçalhos de requisição Cache-Control: no-cache e/ou Pragma: no-cache estiverem presentes.
  2. srcache_store, ou seja, a operação de armazenamento em cache, será ignorada quando o cabeçalho de requisição Cache-Control: no-store for especificado.

Desativar esta diretiva desabilitará essa funcionalidade e é considerado mais seguro para sites movimentados que dependem principalmente do cache para velocidade.

Esta diretiva foi introduzida pela primeira vez no lançamento da v0.12rc7.

Veja também 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

Quando esta diretiva está ativada, os cabeçalhos de resposta Cache-Control e Expires serão respeitados por este módulo das seguintes maneiras:

Esta diretiva tem prioridade sobre as diretivas srcache_store_no_store, srcache_store_no_cache e srcache_store_private.

Esta diretiva foi introduzida pela primeira vez no lançamento da v0.12rc7.

Veja também 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

Ativar esta diretiva forçará respostas com o cabeçalho Cache-Control: no-store a serem armazenadas no cache quando srcache_response_cache_control estiver ativada e outras condições forem atendidas. Padrão para off.

Esta diretiva foi introduzida pela primeira vez no lançamento da 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

Ativar esta diretiva forçará respostas com o cabeçalho Cache-Control: no-cache a serem armazenadas no cache quando srcache_response_cache_control estiver ativada e outras condições forem atendidas. Padrão para off.

Esta diretiva foi introduzida pela primeira vez no lançamento da v0.12rc7.

srcache_store_private

syntax: srcache_store_private on|off

default: srcache_store_private off

context: http, server, location

phase: output-header-filter

Ativar esta diretiva forçará respostas com o cabeçalho Cache-Control: private a serem armazenadas no cache quando srcache_response_cache_control estiver ativada e outras condições forem atendidas. Padrão para off.

Esta diretiva foi introduzida pela primeira vez no lançamento da 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 diretiva controla o período de expiração padrão que é permitido para o valor da variável $srcache_expire quando nem Cache-Control: max-age=N nem Expires são especificados nos cabeçalhos de resposta.

Os valores do argumento <time> estão em segundos por padrão. Mas é sábio sempre especificar explicitamente a unidade de tempo para evitar confusão. As unidades de tempo suportadas são "s" (segundos), "ms" (milissegundos), "y" (anos), "M" (meses), "w" (semanas), "d" (dias), "h" (horas) e "m" (minutos). Por exemplo,

 srcache_default_expire 30m; # 30 minutos

Esse tempo deve ser menor que 597 horas.

A semântica de um tempo de expiração zero depende do backend de armazenamento de cache que você está usando atualmente, que é indiferente a este módulo. No caso do memcached, por exemplo, tempos de expiração zero significam que o item nunca expirará.

Esta diretiva foi introduzida pela primeira vez no lançamento da 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 diretiva controla o período máximo de expiração que é permitido para o valor da variável $srcache_expire. Esta configuração tem prioridade sobre outros métodos de cálculo.

Os valores do argumento <time> estão em segundos por padrão. Mas é sábio sempre especificar explicitamente a unidade de tempo para evitar confusão. As unidades de tempo suportadas são "s" (segundos), "ms" (milissegundos), "y" (anos), "M" (meses), "w" (semanas), "d" (dias), "h" (horas) e "m" (minutos). Por exemplo,

 srcache_max_expire 2h;  # 2 horas

Esse tempo deve ser menor que 597 horas.

Quando 0 é especificado, que é a configuração padrão, então não haverá nenhum limite.

Esta diretiva foi introduzida pela primeira vez no lançamento da v0.12rc7.

Variáveis

$srcache_expire

type: integer

cacheable: no

writable: no

Esta variável do Nginx fornece o período de tempo de expiração recomendado (em segundos) para a resposta atual sendo armazenada no cache. O algoritmo de cálculo do valor é o seguinte:

  1. Quando o cabeçalho de resposta Cache-Control: max-age=N é especificado, então N será usado como o tempo de expiração,
  2. caso contrário, se o cabeçalho de resposta Expires for especificado, então o tempo de expiração será obtido subtraindo o timestamp atual do tempo especificado no cabeçalho Expires,
  3. quando nem os cabeçalhos Cache-Control: max-age=N nem Expires são especificados, use o valor especificado na diretiva srcache_default_expire.

O valor final desta variável será o valor especificado pela diretiva srcache_max_expire se o valor obtido no algoritmo acima exceder o valor máximo (se houver).

Você não precisa usar esta variável para o tempo de expiração.

Esta variável foi introduzida pela primeira vez no lançamento da v0.12rc7.

$srcache_fetch_status

type: string

cacheable: no

writable: no

Esta variável do Nginx é avaliada para o status da fase "fetch" para o sistema de cache. Três valores são possíveis: HIT, MISS e BYPASS.

Quando a subrequisição "fetch" retorna um código de status diferente de 200 ou seus dados de resposta não estão bem formados, então esta variável é avaliada para o valor MISS.

O valor desta variável só é significativo após a fase de processamento da requisição access, ou BYPASS é sempre dado.

Esta variável foi introduzida pela primeira vez no lançamento da v0.14.

$srcache_store_status

type: string

cacheable: no

writable: no

Esta variável do Nginx fornece o status atual de cache para a fase "store". Dois valores possíveis, STORE e BYPASS, podem ser obtidos.

Como as respostas para a subrequisição "store" são sempre descartadas, o valor desta variável será sempre STORE, desde que a subrequisição "store" seja realmente emitida.

O valor desta variável só é significativo pelo menos quando os cabeçalhos de requisição da requisição atual (principal) estão sendo enviados. O resultado final só pode ser obtido após todo o corpo da resposta ter sido enviado se o cabeçalho de resposta Content-Length não for especificado para a requisição principal.

Esta variável foi introduzida pela primeira vez no lançamento da v0.14.

Problemas Conhecidos

Advertências

  • É recomendado desativar a compressão gzip do seu servidor backend e usar o ngx_http_gzip_module do nginx para fazer o trabalho. No caso do ngx_http_proxy_module, você pode usar a seguinte configuração para desativar a compressão gzip do backend:
     proxy_set_header  Accept-Encoding  "";
    
  • Não use a diretiva if do ngx_http_rewrite_module no mesmo local que este módulo, porque "if é o mal". Em vez disso, use o ngx_http_map_module ou o lua-nginx-module combinado com as diretivas srcache_store_skip e/ou srcache_fetch_skip deste módulo. Por exemplo:
     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/...
         }
     }
    

Solução de Problemas

Para depurar problemas, você deve sempre verificar seu arquivo error.log do Nginx primeiro. Se nenhuma mensagem de erro for impressa, você precisa habilitar os logs de depuração do Nginx para obter mais detalhes, como explicado em debugging log.

Vários erros comuns para iniciantes:

  • A resposta original carrega um cabeçalho Cache-Control que desabilita explicitamente o cache e você não configura diretivas como srcache_response_cache_control.
  • A resposta original já está comprimida em gzip, o que não é armazenado em cache por padrão (veja srcache_ignore_content_encoding).
  • O Memcached pode retornar CLIENT_ERROR bad command line format ao usar uma chave muito longa (250 caracteres a partir da versão 1.4.25). Portanto, é mais seguro usar set_md5 $key $uri$args; em vez de set $key $uri$args;. A diretiva set_md5 (e mais) está disponível no módulo set-misc do OpenResty.
  • O Nginx pode retornar client intended to send too large body ao tentar armazenar objetos maiores que 1m no backend de armazenamento, caso em que client_max_body_size do nginx deve ser definido para um valor maior.
  • O Memcached pode falhar ao armazenar objetos maiores que 1m, causando erros como srcache_store subrequest failed status=502. Desde a versão 1.4.2, o memcached suporta uma opção de linha de comando -I para substituir o tamanho padrão de cada página de slab. Por favor, leia sua página de manual para mais informações.

Conjunto de Testes

Este módulo vem com um conjunto de testes dirigido por Perl. Os casos de teste também são declarativos. Graças ao módulo Test::Nginx no mundo Perl.

Para executá-lo do seu lado:

 $ PATH=/path/to/your/nginx-with-srcache-module:$PATH prove -r t
Você precisa encerrar quaisquer processos do Nginx antes de executar o conjunto de testes se você tiver alterado o binário do servidor Nginx.

Como um único servidor nginx (por padrão, localhost:1984) é usado em todos os scripts de teste (.t files), é sem sentido executar o conjunto de testes em paralelo especificando -jN ao invocar a utilidade prove.

Algumas partes do conjunto de testes requerem que os módulos ngx_http_rewrite_module, echo-nginx-module, rds-json-nginx-module e drizzle-nginx-module estejam habilitados também ao construir o Nginx.

Veja Também

GitHub

Você pode encontrar dicas adicionais de configuração e documentação para este módulo no repositório GitHub do nginx-module-srcache.