Todos sabemos que o desempenho de aplicações e sites é um fator crucial para o sucesso deles. No entanto, nem sempre é fácil saber como melhorar o desempenho da sua aplicação ou do seu site. A qualidade do código e a infraestrutura são, obviamente, fundamentais, mas, em muitos casos, é possível melhorar significativamente a experiência do usuário final concentrando-se em algumas técnicas básicas de entrega de aplicações. Um exemplo é implementar e otimizar o armazenamento em cache na pilha da sua aplicação. Esta publicação aborda técnicas que podem ajudar tanto usuários iniciantes quanto avançados a obter um desempenho melhor usando os recursos de cache de conteúdo incluídos no NGINX e no NGINX Plus.
Visão geral
Um cache de conteúdo fica entre um cliente e um “servidor de origem” e salva cópias de todo o conteúdo que recebe. Se um cliente solicitar um conteúdo armazenado no cache, ele será retornado diretamente, sem que o servidor de origem seja contatado. Isso melhora o desempenho, pois o cache de conteúdo está mais próximo do cliente, e permite usar os servidores de aplicação com mais eficiência, já que eles não precisam gerar as páginas do zero a cada solicitação.
Pode haver vários caches entre o navegador e o servidor de aplicações: o cache do navegador do cliente, caches intermediários, redes de distribuição de conteúdo (CDNs) e o balanceador de carga ou proxy reverso localizado à frente dos servidores de aplicações. O uso de cache, mesmo no nível do proxy reverso/balanceador de carga, pode melhorar significativamente o desempenho.
Como exemplo, no ano passado assumi a tarefa de otimizar o desempenho de um site que estava carregando lentamente. Uma das primeiras coisas que notei foi que levava mais de 1 segundo para gerar a página inicial. Após algumas investigações, descobri que, como a página estava marcada para não ser armazenada em cache, ela era gerada dinamicamente a cada solicitação. A página em si não mudava com muita frequência nem era personalizada, portanto isso não era necessário. Como experimento, configurei o balanceador de carga para armazenar a página inicial em cache por 5 segundos, e só isso já resultou em uma melhora perceptível. O tempo até o primeiro byte caiu para alguns milissegundos, e a página passou a carregar visivelmente mais rápido.
O NGINX é comumente implantado como proxy reverso ou balanceador de carga em uma pilha de aplicações e oferece um conjunto completo de recursos de cache. A próxima seção explica como configurar o cache básico com o NGINX.
Como instalar e configurar o cache básico
Apenas duas diretivas são necessárias para habilitar o cache básico: proxy_cache_path e proxy_cache. A diretiva proxy_cache_path define o caminho e a configuração do cache, e a diretiva proxy_cache o ativa.
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;
}
}Os parâmetros da diretiva proxy_cache_path definem as seguintes configurações:
- O diretório no disco local destinado ao cache é chamado de /path/to/cache/.
levelsconfigura uma hierarquia de diretórios de dois níveis em /path/to/cache/. Ter um grande número de arquivos em um único diretório pode tornar o acesso aos arquivos mais lento. Por isso, recomendamos uma hierarquia de diretórios de dois níveis para a maioria das implantações. Se o parâmetrolevelsnão for incluído, o NGINX colocará todos os arquivos no mesmo diretório.keys_zoneconfigura uma zona de memória compartilhada para armazenar as chaves do cache e metadados, como temporizadores de uso. Manter uma cópia das chaves na memória permite que o NGINX determine rapidamente se uma solicitação é umHITou umMISS, sem precisar acessar o disco, o que agiliza bastante a verificação. Uma zona de 1 MB pode armazenar dados de cerca de 8.000 chaves; portanto, a zona de 10 MB configurada no exemplo pode armazenar dados de cerca de 80.000 chaves.max_sizedefine o limite máximo do tamanho do cache (10 gigabytes neste exemplo). Esse parâmetro é opcional; se nenhum valor for especificado, o cache poderá crescer até ocupar todo o espaço disponível em disco. Quando o tamanho do cache atinge o limite, um processo chamado gerenciador de cache remove os arquivos usados há mais tempo para reduzir o tamanho do cache e deixá-lo novamente abaixo do limite.inactiveespecifica por quanto tempo um item pode permanecer no cache sem ser acessado. Neste exemplo, um arquivo que não tenha sido solicitado por 60 minutos é excluído automaticamente do cache pelo processo gerenciador de cache, independentemente de ter expirado ou não. O valor padrão é 10 minutos (10m). O conteúdo inativo é diferente do conteúdo expirado. O NGINX não exclui automaticamente o conteúdo que expirou conforme definido por um cabeçalho de controle de cache (Cache-Control:max-age=120, por exemplo). O conteúdo expirado (obsoleto) só é excluído quando não é acessado durante o período especificado porinactive. Quando o conteúdo expirado é acessado, o NGINX o atualiza a partir do servidor de origem e reinicia o temporizador deinactive.- Primeiro, o NGINX grava os arquivos destinados ao cache em uma área de armazenamento temporário, e a diretiva
use_temp_path=offinstrui o NGINX a gravá-los nos mesmos diretórios em que serão armazenados em cache. Recomendamos definir esse parâmetro comooffpara evitar a cópia desnecessária de dados entre sistemas de arquivos. O parâmetrouse_temp_pathfoi introduzido no NGINX versão 1.7.10 e no NGINX Plus R6.
E, por fim, a diretiva proxy_cache ativa o armazenamento em cache de todo o conteúdo que corresponde à URL do bloco location pai (no exemplo, /). Você também pode incluir a diretiva proxy_cache em um bloco server; ela se aplica a todos os blocos location do servidor que não tenham sua própria diretiva proxy_cache.
Fornecimento de conteúdo em cache quando o servidor de origem está indisponível
Um recurso avançado do cache de conteúdo do NGINX é a possibilidade de configurá-lo para fornecer conteúdo desatualizado do cache quando não consegue obter conteúdo atualizado dos servidores de origem. Isso pode acontecer quando todos os servidores de origem de um recurso armazenado em cache estão indisponíveis ou temporariamente ocupados. Em vez de repassar o erro ao cliente, o NGINX fornece a versão desatualizada do arquivo armazenada em cache. Isso oferece um nível adicional de tolerância a falhas para os servidores aos quais o NGINX atua como proxy e garante a disponibilidade em caso de falhas nos servidores ou picos de tráfego. Para habilitar essa funcionalidade, inclua a diretiva proxy_cache_use_stale:
location / { # ...
proxy_cache_use_stale error timeout http_500 http_502 http_503 http_504;
}Com esta configuração de exemplo, se o NGINX receber um error, timeout, ou qualquer um dos erros 5xx especificados do servidor de origem e tiver uma versão desatualizada do arquivo solicitado em seu cache, ele entregará o arquivo desatualizado em vez de repassar o erro ao cliente.
Ajustando o cache e melhorando o desempenho
O NGINX oferece diversas configurações opcionais para ajustar o desempenho do cache. Veja um exemplo que ativa algumas delas:
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;
}
}Essas diretivas configuram o seguinte comportamento:
proxy_cache_revalidateinstrui o NGINX a usar solicitações condicionaisGETao atualizar o conteúdo dos servidores de origem. Se um cliente solicitar um item que esteja armazenado em cache, mas tenha expirado conforme definido pelos cabeçalhos de controle de cache, o NGINX incluirá o campoIf-Modified-Sinceno cabeçalho da solicitaçãoGETenviada ao servidor de origem. Isso economiza largura de banda, pois o servidor envia o item completo somente se ele tiver sido modificado desde o momento registrado no cabeçalhoLast-Modifiedanexado ao arquivo quando o NGINX o armazenou originalmente em cache.proxy_cache_min_usesdefine o número de vezes que um item deve ser solicitado pelos clientes antes que o NGINX o armazene em cache. Isso é útil quando o cache está constantemente ficando cheio, pois garante que apenas os itens acessados com mais frequência sejam adicionados ao cache. Por padrão,proxy_cache_min_usesé definido como 1.- O parâmetro
updatingda diretivaproxy_cache_use_stale, combinado com a ativação da diretivaproxy_cache_background_update, instrui o NGINX a fornecer conteúdo desatualizado quando os clientes solicitam um item que expirou ou está sendo atualizado a partir do servidor de origem. Todas as atualizações serão realizadas em segundo plano. O arquivo desatualizado será retornado para todas as solicitações até que o download do arquivo atualizado seja concluído. - Com o
proxy_cache_lockhabilitado, se vários clientes solicitarem um arquivo que não esteja atualizado no cache (umMISS), somente a primeira dessas solicitações poderá ser encaminhada ao servidor de origem. As solicitações restantes aguardarão até que essa solicitação seja atendida e, em seguida, obterão o arquivo do cache. Sem oproxy_cache_lockhabilitado, todas as solicitações que resultarem em falhas de cache serão encaminhadas diretamente ao servidor de origem.
Distribuindo o cache entre vários discos rígidos
Se você tiver vários discos rígidos, o NGINX poderá ser usado para distribuir o cache entre eles. Veja um exemplo que distribui os clientes uniformemente entre dois discos rígidos com base no URI da solicitação:
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;
}
}As duas diretivas proxy_cache_path definem dois caches (my_cache_hdd1 e my_cache_hdd2) em dois discos rígidos diferentes. O bloco de configuração split_clients especifica que os resultados de metade das solicitações (50%) são armazenados em cache em my_cache_hdd1 e os da outra metade em my_cache_hdd2. O hash baseado na variável $request_uri (o URI da solicitação) determina qual cache é usado para cada solicitação, fazendo com que as solicitações de um determinado URI sejam sempre armazenadas no mesmo cache.
Observe que essa abordagem não substitui uma configuração de discos rígidos em RAID. Se ocorrer uma falha em um disco rígido, isso poderá causar um comportamento imprevisível no sistema, inclusive fazer com que os usuários recebam códigos de resposta 500 para solicitações direcionadas ao disco rígido com falha. Uma configuração adequada de discos rígidos em RAID é capaz de lidar com falhas de disco.
Perguntas frequentes (FAQ)
Esta seção responde a algumas perguntas frequentes sobre o cache de conteúdo do NGINX.
É possível instrumentar o cache do NGINX?
Sim, com a diretiva add_header:
add_header X-Cache-Status $upstream_cache_status;Este exemplo adiciona um cabeçalho HTTP X-Cache-Status às respostas enviadas aos clientes. Estes são os possíveis valores de $upstream_cache_status:
MISS– A resposta não foi encontrada no cache e, por isso, foi obtida de um servidor de origem. Em seguida, a resposta pode ter sido armazenada em cache.BYPASS– A resposta foi obtida do servidor de origem em vez de ser fornecida pelo cache porque a solicitação correspondeu a uma diretivaproxy_cache_bypass(consulte Posso abrir uma exceção no meu cache? abaixo). A resposta pode ter sido armazenada em cache posteriormente.EXPIRED– A entrada no cache expirou. A resposta contém conteúdo atualizado do servidor de origem.STALE– O conteúdo está desatualizado porque o servidor de origem não está respondendo corretamente, eproxy_cache_use_stalefoi configurado.UPDATING– O conteúdo está desatualizado porque a entrada está sendo atualizada no momento em resposta a uma solicitação anterior, eproxy_cache_use_stale updatingestá configurado.REVALIDATED– A diretivaproxy_cache_revalidatefoi habilitada, e o NGINX verificou que o conteúdo atualmente armazenado em cache ainda era válido (If-Modified-SinceouIf-None-Match).HIT– A resposta contém conteúdo válido e atualizado, fornecido diretamente do cache.
Como o NGINX determina se deve ou não armazenar algo em cache?
O NGINX armazena uma resposta em cache somente se o servidor de origem incluir o cabeçalho Expires com uma data e hora futuras ou o cabeçalho Cache-Control com a diretiva max-age definida com um valor diferente de zero.
Por padrão, o NGINX respeita outras diretivas no cabeçalho Cache-Control: ele não armazena respostas em cache quando o cabeçalho inclui a diretiva Private, No-Cache ou No-Store. Ele também não armazena em cache respostas com o cabeçalho Set-Cookie. Além disso, ele armazena em cache apenas respostas a solicitações GET e HEAD. Você pode substituir esses padrões conforme descrito nas respostas abaixo.
O NGINX não armazena respostas em cache se proxy_buffering estiver definido como off. Por padrão, ele fica on.
É possível ignorar os cabeçalhos Cache-Control?
Sim, com a diretiva proxy_ignore_headers. Por exemplo, com esta configuração:
location /images/ {
proxy_cache my_cache;
proxy_ignore_headers Cache-Control;
proxy_cache_valid any 30m;
# ...
}O NGINX ignora o cabeçalho Cache-Control para tudo em /images/. A diretiva proxy_cache_valid define um prazo de expiração para os dados armazenados em cache e é obrigatória quando os cabeçalhos Cache-Control são ignorados. O NGINX não armazena em cache arquivos que não têm prazo de expiração.
O NGINX pode armazenar em cache conteúdo com um Set-Cookie no cabeçalho?
Sim, com a diretiva proxy_ignore_headers, conforme discutido na resposta anterior.
O NGINX pode armazenar em cache solicitações POST?
Sim, com a diretiva proxy_cache_methods:
proxy_cache_methods GET HEAD POST;Este exemplo habilita o armazenamento em cache de solicitações POST.
O NGINX pode armazenar conteúdo dinâmico em cache?
Sim, desde que o cabeçalho Cache-Control permita. Armazenar conteúdo dinâmico em cache, mesmo que por um curto período, pode reduzir a carga nos servidores de origem e nos bancos de dados, melhorando o tempo até o primeiro byte, pois a página não precisa ser gerada novamente a cada solicitação.
Posso abrir uma exceção no meu cache?
Sim, com a diretiva proxy_cache_bypass:
location / {
proxy_cache_bypass $cookie_nocache $arg_nocache;
# ...
}A diretiva define os tipos de solicitação para os quais o NGINX solicita conteúdo imediatamente ao servidor de origem, em vez de primeiro tentar encontrá-lo no cache. Isso às vezes é chamado de “abrir um furo” no cache. Neste exemplo, o NGINX faz isso para solicitações com um cookie ou argumento nocache, por exemplo, http://www.example.com/?nocache=true. O NGINX ainda pode armazenar em cache a resposta resultante para futuras solicitações que não ignorem o cache.
Qual chave de cache o NGINX usa?
O formato padrão das chaves geradas pelo NGINX é semelhante a um hash MD5 das seguintes variáveis do NGINX: $scheme$proxy_host$request_uri; o algoritmo usado é, na verdade, um pouco mais complexo.
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 configuração de exemplo, a chave de cache para http://www.example.org/my_image.jpg é calculada como md5(“http://my_upstream:80/my_image.jpg”).
Observe que a variável $proxy_host é usada no valor de hash em vez do nome real do host (www.example.com). $proxy_host é definida como o nome e a porta do servidor que recebe o proxy, conforme especificado na diretiva proxy_pass.
Para alterar as variáveis (ou outros termos) usadas como base para a chave, use a diretiva proxy_cache_key (consulte também a pergunta a seguir).
Posso usar um cookie como parte da minha chave de cache?
Sim, a chave de cache pode ser configurada com qualquer valor arbitrário, por exemplo:
proxy_cache_key $proxy_host$request_uri$cookie_jessionid;Este exemplo incorpora o valor do cookie JSESSIONID à chave de cache. Itens com o mesmo URI, mas com valores de JSESSIONID diferentes, são armazenados em cache separadamente como itens únicos.
O NGINX usa o cabeçalho ETag?
No NGINX 1.7.3 e no NGINX Plus R5 e versões posteriores, o cabeçalho ETag é totalmente compatível, assim como o If-None-Match.
Como o NGINX processa solicitações de intervalo de bytes?
Se o arquivo estiver atualizado no cache, o NGINX atenderá à solicitação de intervalo de bytes e enviará ao cliente apenas os bytes especificados do item. Se o arquivo não estiver armazenado em cache ou estiver desatualizado, o NGINX baixará o arquivo inteiro do servidor de origem. Se a solicitação for referente a um único intervalo de bytes, o NGINX enviará esse intervalo ao cliente assim que ele for encontrado no fluxo de download. Se a solicitação especificar vários intervalos de bytes no mesmo arquivo, o NGINX enviará o arquivo inteiro ao cliente quando o download for concluído.
Após a conclusão do download, o NGINX move o recurso inteiro para o cache, de modo que todas as solicitações futuras de intervalos de bytes, sejam elas de um único intervalo ou de vários intervalos, sejam atendidas imediatamente pelo cache.
Observe que o servidor upstream deve oferecer suporte a solicitações de intervalo de bytes para que o NGINX atenda a solicitações de intervalo de bytes destinadas a esse servidor upstream.
O NGINX oferece suporte à limpeza de cache?
NGINX Plus oferece suporte à limpeza seletiva de arquivos armazenados em cache. Isso é útil quando um arquivo foi atualizado no servidor de origem, mas ainda é válido no cache do NGINX Plus (o Cache-Control:max-age ainda é válido e o tempo limite definido pelo parâmetro inactive da diretiva proxy_cache_path ainda não expirou). Com o recurso de limpeza de cache do NGINX Plus, esse arquivo pode ser excluído facilmente. Para obter mais detalhes, consulte Remoção de conteúdo do cache.
Como o NGINX processa o cabeçalho Pragma?
O cabeçalho Pragma:no-cache é adicionado pelos clientes para ignorar todos os caches intermediários e acessar diretamente o servidor de origem para obter o conteúdo solicitado. Por padrão, o NGINX não considera o cabeçalho Pragma, mas você pode configurar esse recurso com a seguinte diretiva proxy_cache_bypass:
location /images/ {
proxy_cache my_cache;
proxy_cache_bypass $http_pragma;
# ...
}O NGINX oferece suporte às extensões stale-while-revalidate e stale-if-error do cabeçalho Cache-Control?
Sim, no NGINX Plus R12 e no NGINX 1.11.10 e versões posteriores. Veja o que essas extensões fazem:
- A extensão
stale-while-revalidatedo cabeçalho HTTPCache-Controlpermite usar uma resposta obsoleta armazenada em cache enquanto ela estiver sendo atualizada. - A extensão
stale-if-errordo cabeçalho HTTPCache-Controlpermite usar uma resposta obsoleta armazenada em cache em caso de erro.
Esses cabeçalhos têm prioridade menor que a diretiva proxy_cache_use_stale descrita acima.
O NGINX oferece suporte ao cabeçalho Vary?
Sim, no NGINX Plus R5 e no NGINX 1.7.7 e versões posteriores. Veja uma boa visão geral do cabeçalho Vary header.
Leitura adicional
Há muitas outras maneiras de personalizar e ajustar o cache do NGINX. Para saber ainda mais sobre o uso de cache com o NGINX, consulte os seguintes recursos:
- A documentação de referência do ngx_http_proxy_module contém todas as opções de configuração para o cache de conteúdo.
- Dois webinars estão disponíveis sob demanda e abordam grande parte das informações apresentadas nesta publicação do blog: Cache de conteúdo com NGINX Plus e Cache de conteúdo de alta disponibilidade com NGINX.
- O Guia do Administrador do NGINX Plus contém mais exemplos de configuração e informações sobre como ajustar o cache do NGINX.
- A página do produto Cache do NGINX Plus apresenta uma visão geral de como configurar a limpeza do cache com o NGINX Plus, além de outros exemplos de personalização do cache.
- O eBook Cache de alto desempenho com NGINX e NGINX Plus apresenta uma análise aprofundada do cache de conteúdo do NGINX.
Sobre o autor

Publicações relacionadas no blog

Como o F5 NGINX One aprimora o gerenciamento e a observabilidade
Descubra como o F5 NGINX One aprimora o gerenciamento e a observabilidade de ambientes híbridos e multicloud ao oferecer um console web centralizado baseado em SaaS, com insights em tempo real, conformidade automatizada e gerenciamento simplificado de configurações.

Do OpenAPI ao NGINX como gateway de API usando uma API declarativa
Descubra como transformar uma definição de esquema OpenAPI em uma configuração NGINX totalmente funcional, operando como um API Gateway com a segurança de um Web Application Firewall e um Portal do Desenvolvedor, usando uma abordagem declarativa de API.

Como gerenciar picos repentinos de tráfego e sobrecarga do servidor
Picos de tráfego e sobrecarga de servidores são preocupações sérias para varejistas online em qualquer época, mas especialmente durante a “Cyber Season” de compras de fim de ano. Detalhamos como você pode aproveitar as soluções de entrega de aplicações de nível de produção da NGINX para se preparar para aumentos repentinos de tráfego.

Distribuição segura de chaves privadas SSL com o NGINX
Descrevemos três maneiras progressivamente mais seguras de proteger chaves privadas SSL ao configurar o NGINX para processar tráfego HTTPS: permitir acesso de leitura apenas ao usuário root, criptografar as chaves com senhas armazenadas separadamente e distribuir as senhas a partir de um repositório central.
Agrupando solicitações de API em lotes com NGINX Plus e o módulo JavaScript do NGINX
O módulo JavaScript do NGINX lançado com o NGINX Plus R15 oferece suporte a subsolicitações, possibilitando uma classe totalmente nova de casos de uso para o NGINX Plus. Este exemplo completo também usa a API do NGINX Plus e os módulos de chave-valor para converter solicitações de API dos clientes em solicitações separadas para as APIs dos microsserviços que compõem um aplicativo.
Como superar o esgotamento de portas efêmeras no NGINX e no NGINX Plus
Confira dicas para evitar o esgotamento de portas efêmeras com ajustes no kernel do Linux, conexões keepalive e conexões vinculadas dinamicamente a endereços IP locais.