Português do Brasil English
Devin no Facebook  Devin no Twitter  RSS do Site 
Servidores    

Uma introdução ao Varnish


Comentários  17
Visualizações  
204,972

Um dos problemas mais clássicos dos sistemas dinâmicos na Web é a carga que estes sistemas causam nos servidores. Diferentemente de servir apenas itens estáticos, os conteúdos dinâmicos precisam interpretar códigos, transformá-los, acessar bancos de dados, entre outras operações que tornam a carga de sistema maior. O Varnish é um software de código aberto que serve como uma camada intermediária entre o usuário e o servidor Web. Ele funciona fazendo cache do conteúdo dinâmico, transformando-o em uma página estática em memória, e então mandando para o usuário. Com isso, para todo usuário seguinte, o Varnish serve a página que já está em cache, ao invés do sistema dinâmico ter que efetuar todo o processamento novamente.

Com essa camada de cache, a maioria das requisições são servidas diretamente da camada intermediária, e por isso o site não perde desempenho ao ter quantidades grandes de requisições ao mesmo tempo. Por exemplo, se eu configuro o Varnish para fazer cache por 30 segundos de uma página que tem 500 acessos por segundo de um WordPress, o WordPress só vai ter que gerar a página apenas uma vez a cada 30 segundos, servindo um total de 15 mil requisições de clientes com apenas uma operação dinâmica do WordPress.

Neste tutorial, vamos aprender como começar a mexer com esse incrível serviço que é o Varnish!

Instalação

Na maioria das vezes você pode instalar o Varnish a partir dos pacotes da sua distribuição favorita. Caso você precise de uma versão mais nova que a que vem na distribuição, pode optar por baixar os pacotes separadamente no próprio site do Varnish, onde tem versões para Debian, FreeBSD, RHEL e Ubuntu. Também tem o código-fonte caso queira compilar.

O Varnish é bastante stand-alone, ou seja, ele depende muito pouco de outros pacotes. Se você for compilar, vai precisar de glibc, pcre, gcc (para compilar o VCL em tempo real) e um não tão convencional chamado jemalloc (uma biblioteca alocadora de memória). Tendo estas poucas dependências, os convencionais ./configure, make e make install irão funcionar.

Neste tutorial, vou usar as localizações genéricas dos arquivos de configuração. Lembre-se de substituir caso você tenha usado algo diferente no ./configure ou se sua distribuição resolveu usar algo não-convencional.

Primeiros Passos

Pra começar, você vai precisar de um servidor Web e um sistema dinâmico funcionando. Pode ser qualquer coisa dinâmica. Como eu gosto muito de WordPress, aqui usarei como base uma instalação simples de Apache 2, PHP e WordPress. Use o que for mais interessante pra você!

Na maioria das distribuições, você só vai precisar de um:

service varnish start

Ou um:

/bin/systemctl start varnish.service

Ou um:

/etc/init.d/varnish start

Estes comandos iniciam o serviço lendo um arquivo de configuração do sistema e executando o comando varnishd. Então esqueça estes comandos por enquanto, e vamos iniciar manualmente:

varnishd -a :81 -T 127.0.0.1:82 -s file,/var/lib/varnish/varnish_storage.bin,1G -f /etc/varnish/default.vcl

Este comando nos diz:

  • -a :81 – O varnish vai funcionar como clinte web na porta 81 de todos os IPs da máquina;
  • -T 127.0.0.1:82 – A interface de gerenciamento funcionará na porta 82 do localhost apenas;
  • -s file,<arquivo>,1G – O cache será armazenado em arquivo, com tamanho máximo de 1GB;
  • -f – A configuração inicial usada é a do arquivo “VCL” /etc/varnish/default.vcl.

Isso quer dizer que se você executar um wget em http://localhost:81/, estará requisitando ao Varnish. A configuração padrão do Varnish tenta buscar as páginas da porta 80 em localhost (nosso web server local), e faz cache de todas as requisições GET, a não ser as que tem cookies ou autorização http.

As opções que usamos são bem básicas, e também se encontram no arquivo de configuração do sistema que falei anteriormente. Em sistemas baseados em Debian/Ubuntu, fica em /etc/default/varnish. Em sistemas baseados em RHEL/Fedora, fica em /etc/sysconfig/varnish (e mais posteriormente com o systemd, /usr/lib/systemd/system/varnish.service e /etc/varnish/varnish.params).

Nestes testes de execução do serviço manualmente, quando quiser parar o varnish, basta um:

killall -TERM varnish

VCL

O VCL (Linguagem de Configuração do Varnish, ou em inglês, Varnish Configuration Language) é uma linguagem própria que o Varnish usa para decidir o que fazer quando as requisições chegam à ele. Em um arquivo VCL, você pode usar toda a flexibilidade que o Varnish oferece, como por exemplo: definir backends e balanceamentos, definir políticas de cache, mexer nos cabeçalhos HTTP, entre muitas outras características. Grande parte da diversão está aqui!

No exemplo anterior, usamos o /etc/varnish/default.vcl, que contém as regras padrões. Repare que dentro deste arquivo, por padrão, as linhas estão todas comentadas (menos as que definem o backend), ou seja, na nossa primeira execução esse arquivo não serviu para nada. Mas estas regras comentadas no default.vcl contém as regras que já vem dentro do código do varnish e são carregadas caso você não especifique nada. O arquivo existe para você ter uma referência fácil destas regras e para poder reutilizá-las caso necessite.

Vamos criar um arquivo do zero, chamado /etc/varnish/tutorial.vcl:

backend default {
  .host = "127.0.0.1";
  .port = "80";
}

sub vcl_recv {
  if (req.request == "GET") {
    return (lookup);
  }
}

sub vcl_fetch {
  set beresp.ttl = 30s;
  return (deliver);
}

Depois de salvar o arquivo, vamos carregá-lo dentro do Varnish. Para fazer isso, não precisamos exatamente reiniciar o serviço, podemos simplesmente carregar o novo VCL dentro do Varnish que já está rodando. Uma das funcionalidades da interface de gerenciamento que colocamos na porta 82 é justamente essa. Conecte nela usando o comando telnet:

$ telnet localhost 82
Trying 127.0.0.1...
Connected to localhost.
Escape character is '^]'.
200 208    
-----------------------------
Varnish Cache CLI 1.0
-----------------------------
Linux,3.7.7-201.fc18.x86_64,x86_64,-sfile,-smalloc,-hcritbit

Type 'help' for command list.
Type 'quit' to close CLI session.

A partir daqui você pode usar vários comandos de gerenciamento do Varnish (o comando help te lista quais são eles). Vamos primeiro carregar o VCL do nosso tutorial:

vcl.load tutorial /etc/varnish/tutorial.vcl
200 13     
VCL compiled.

Com o comando vcl.load, carreguei o nosso arquivo e dei o nome de referência dele como tutorial. Repare que o varnish retornou a mensagem “VCL compilado”. Compilado? Como assim? Pois, toda vez que o Varnish carrega um VCL, ele compila da linguagem dele para a de máquina, o que deixa tudo mais rápido, e é a grande necessidade de se ter um compilador C na mesma máquina que o Varnish.

Agora vamos listar os VCLs compilados e carregados em memória pelo Varnish com o comando vcl.list:

vcl.list
200 50     
active          2 boot
available       0 tutorial

Repare que há dois: o de boot (quando iniciamos o serviço, ou seja, o default.vcl) e o novo, do tutorial. Repare também que a linha do “boot” começa com ativo, então o Varnish está usando ainda a configuração antiga. Para ativar a nova, usamos o vcl.use:

vcl.use tutorial
200 0   
vcl.list
200 50     
available       2 boot
active          0 tutorial

Pronto. Agora quem está ativo é a configuração que nomeamos de tutorial. Este vai e vem é útil para você guardar um histórico de configurações do varnish, e caso precise voltar uma configuração, basta utilizar o vcl.use.

Para sair da interface de gerencimaneto, basta dar o comando quit.

Atenção: No momento que você usar o vcl.load, todas as modificações no arquivo em si não surtirão efeito nenhum, a não ser que você o carregue com vcl.load. Como dito anteriormente, o varnish olha o arquivo uma vez e o compila em memória.

Agora vamos avaliar nossa nova configuração. Eu não disse até agora, mas o tutorial.vcl configura o Varnish para cachear qualquer requisição GET por 30 segundos. E ele só faz isso. Escolha um arquivo qualquer do seu site dinâmico e pegue-o com o wget (o -S vai mostrar os cabeçalhos HTTP de resposta), exemplo:

$ wget -O /dev/null -S http://localhost:81/
Resolving localhost (localhost)... 127.0.0.1
Connecting to localhost (localhost)|127.0.0.1|:81... connected.
HTTP request sent, awaiting response...
  HTTP/1.1 [...]
  [...]
  Age: 0

Note que há uma linha começando com “Age: “. Este Age é o cabeçalho HTTP que o Varnish coloca indicando quanto tempo ele está no cache. Quando é 0, significa que o Varnish não tinha a página em cache e por isso teve que buscar no servidor Web.

Agora utilize o comando a seguir para executar o mesmo wget infinitamente, com intervalos de 1 segundo, e preste atenção na linha do cabeçalho Age. Olhe por pelo menos 1 minuto e você deve entender:

$ while true; do wget -O /dev/null -S http://localhost:81/; sleep 1; done

Viu? A cada segundo, o cabeçalho Age vai aumentando, quando chega no número 30, ele zera novamente. Isso quer dizer que o Varnish só busca a página no servidor Web no mínimo de 30 em 30 segundos.

Agora, no meu caso o WordPress está reclamando da porta 81 e redirecionando a requisição para a porta 80. Eu tenho duas opções aqui:

  1. Mudar o Apache para a porta 81 e deixar o Varnish escutando na porta 80 (-a :80);
  2. Deixar assim e tirar a porta dentro do VCL no Varnish.

Na maioria das vezes a primeira solução é a utilizada, mas para fins educacionais divertidos, vou usar o VCL para fazer essa correção… Para isso, tenho que voltar ao tutorial.vcl e adicionar a seguinte linha no começo do vcl_recv:

sub vcl_recv {
  set req.http.host = regsub(req.http.host, ":81", "");

  if (req.request == "GET") {
    return (lookup);
  }
}

Na linha adicionada, eu basicamente retirei o :81 do cabeçalho HTTP, antes de mandar para o backend. Não se preocupe que mais para frente iremos ver com mais detalhes as funcionalidades disponíveis no VCL.

Agora vou carregar essa nova configuração via interface de gerenciamento, e colocarei o nome de tutorial-2:

$ telnet localhost 82
Trying 127.0.0.1...
Connected to localhost.
Escape character is '^]'.
200 208    
-----------------------------
Varnish Cache CLI 1.0
-----------------------------
Linux,3.7.7-201.fc18.x86_64,x86_64,-sfile,-smalloc,-hcritbit

Type 'help' for command list.
Type 'quit' to close CLI session.

vcl.load tutorial-2 /etc/varnish/tutorial.vcl
200 13     
VCL compiled.

vcl.list
200 79     
available       0 boot
active          2 tutorial
available       0 tutorial-2

vcl.use tutorial-2
200 0

(Já deve ter dado para entender o vcl.load, vcl.list e vcl.use, então da próxima vez vou usar algo mais robusto que o telnet.)

Backends

Antes de entrar em detalhes sobre o que podemos fazer no VCL, precisamos entender melhor como funcionam os backends. Um backend, dentro do VCL, é uma referência à um servidor Web que é quem serve as páginas de verdade. Nos exemplos anteriores, utilizamos o backend padrão, que se conecta em localhost, porta 80. No nosso tutorial.vcl, essa definição está nas linhas:

backend default {
  .host = "127.0.0.1";
  .port = "80";
}

E é bem auto-explicativo… Basta mudar os parâmetros .host e .port para o varnish tentar buscar as páginas em outro servidor, por exemplo. Só que mais do que isso, podemos definir vários backends ao mesmo tempo.

Para ilustrar isso, vamos fazer uma brincadeira. Vamos definir novos backends configurados para buscar páginas em servidores da Internet! Assumindo que seu servidor tenha acesso à Internet, é claro. Coloque as seguintes definições de backend depois da default:

backend devin {
  .host = "&lt;ip do www.devin.com.br&gt;";
  .port = "80";
}

.backend google {
  .host = "&lt;ip do www.google.com.br&gt;";
  .port = "80";
}

Nota: resolva o IP dos domínios acima e substitua no parâmetro .host. Isto é necessário pois o Varnish ao compilar o VCL só pode referenciar um IP apenas, e se os domínios retornarem mais de um, ele não vai saber qual colocar exatamente. No momento em que escrevo o tutorial, os IPs podem ser 64.111.124.56 para Devin e 74.125.234.216 para Google.

Feito isto, vamos fazer com que nosso varnish toda vez que receber requisições em /devin/, responda com a página principal desse site. E quando for /google/, responda com a página principal do Google. Lembre-se que é apenas ilustrativo, a página em si e suas sub-páginas não podem funcionar corretamente. O importante aqui é a gente ver a página principal sendo retornada pelo seu varnish.

Deixe o vcl_recv do tutorial.vcl assim:

sub vcl_recv {
  set req.http.host = regsub(req.http.host, ":81", "");

  if (req.url ~ "^/devin/$") {
    set req.http.host = "www.devin.com.br";
    set req.url = regsub(req.url, "^/devin/$", "/");
    set req.backend = devin;
  } elsif (req.url ~ "^/google/$") {
    set req.http.host = "www.google.com.br";
    set req.url = regsub(req.url, "^/google/$", "/");
    set req.backend = google;
  }

  if (req.request == "GET") {
    return (lookup);
  }
}

Recarregue o VCL com o nome “tutorial-3″ (agora com comandos ao invés do telnet):

varnishadm -T localhost:82 vcl.load tutorial-3 /etc/varnish/tutorial.vcl
varnishadm -T localhost:82 vcl.use tutorial-3

(Sim, o varnishadm é um comando que utiliza a interface de gerenciamento diretamente ;-)

Agora antes de pirar com a programação utilizada no VCL, carregue as páginas em um navegador:

  • http://localhost:81/devin/
  • http://localhost:81/google/

As respectivas páginas devem retornar. Caso isso não ocorra, tem algum erro de digitação no meio do caminho :-)

Há algumas coisas novas que usamos no nosso código VCL. No bloco de código novo, temos um teste de condições que nos diz:

  • Caso a URL (variável req.url) da requisição for /devin/, ele vai utilizar o backend devin (set req.backend), mas antes de mandar a requisição pro servidor web, ele vai substituir o /devin/ por / e o cabeçalho host vai virar o www.devin.com.br.
  • Caso a URL (variável req.url) da requisição for /google/, ele vai utilizar o backend google (set req.backend), mas antes de mandar a requisição pro servidor web, ele vai substituir o /google/ por / e o cabeçalho host vai virar o www.google.com.br.

Já está sentindo a pitada de poder do VCL?

Nosso VCL agora, além de fazer cache de todas as páginas por 30 segundos, tem a possibilidade de cachear conteúdos de vários servidores e servir baseado na URL de requisição. A variável req.url, que contém a URL de requisição (como o próprio nome diz), é uma poderosa ferramenta de decisão: com ela você pode usar em um mesmo domínio quantos servidores Web diferentes quiser.

Balanceamento de backends

Com o que aprendemos sobre backends, podemos acrescentar também que o varnish pode ser utilizado não apenas como cache, mas também como balanceador. Isso significa que se você tem mais de um servidor que serve as mesmas páginas, você pode dizer ao Varnish para distribuir a carga entre estes servidores, e se um ou outro falhar, não receber dele.

Para fazer isso de forma eficiente, primeiro temos que adicionar um probe no backend. Esse probe vai ficar testando o backend, para saber se ele está funcionando ou não. Esse prob é uma requisição HTTP que o próprio varnish manda internamente para o backend. Altere o nosso tutorial.vcl e adicione um probe ao backend padrão:

backend default {
  .host = "127.0.0.1";
  .port = "80";
  .probe = {
    .url = "/";
    .timeout = 1s;
    .window = 5;
    .threshold = 3;
    .initial = 3;
  }
}

Recarregue o VCL com o nome de tutorial-4. Depois de carregado, para saber se o probe está funcionando e o backend está funcionando (healthy), use o comando:

$ varnishadm -T localhost:82 debug.health
Backend default is Healthy
Current states  good:  4 threshold:  3 window:  5
Average responsetime of good probes: 0.040417
Oldest                                                    Newest
================================================================
-------------------------------------------------------------444 Good IPv4
-------------------------------------------------------------XXX Good Xmit
--------------------------------------------------------------RR Good Recv
----------------------------------------------------------HHH-HH Happy

$ varnishadm -T localhost:82 debug.health
Backend default is Healthy
Current states  good:  5 threshold:  3 window:  5
Average responsetime of good probes: 0.111121
Oldest                                                    Newest
================================================================
4444444444444444444444444444444444444444444444444444444444444444 Good IPv4
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX Good Xmit
RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRR- Good Recv
HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH- Happy

Eu defini que o backend default terá um probe que perguntará pela URL raíz (url = /). Se o probe não tiver uma resposta em 1 segundo, ele vai falhar (timeout = 1s). O probe considera que o backend esteja funcionando depois que tiver 3 respostas inicias boas (initial = 3). Depois que ele já está funcionando, eu mantenho um histórico de 5 das respostas (window = 5), e determino se o o backend falhou ou não analisando esse histórico e encontrando 3 últimas respostas boas ou ruins (threshold = 3).

O segundo comando eu dei depois de alguns minutos. Note que esse comando utiliza a interface de gerenciamento (a mesma do vcl.load, vcl.use, etc). Cada linha de resposta mostra um histórico dos probes. Quando nestas linhas há um hífen (-), quer dizer que o probe falhou. Quando há uma letra, quer dizer que deu certo.

No nosso caso, o primeiro comando por ter sido feito logo quando ativei o VCL, tem várias linhas com hífen, pois o probe nem existia. Com o tempo, as linhas foram ficando com as letras, indicando que a resposta do IP (ping) está boa (Good IPv4), a transmissão e recebimento de rede via TCP estão boas (Good Xmit e Good Recv) e que a resposta da URL pedida está OK (Happy).

Repare também que só há um probe para o backend default. Não há um para o devin ou google porque não definimos probes para eles.

Agora antes de configurarmos o balanceamento, configure o seu servidor Web para escutar em outras portas, para simular vários servidores. Se você tiver mais servidores, melhor. Aqui neste tutorial, configurei nas portas 90, 91 e 92, todos respondendo para a mesma instalação de WordPress.

Com vários servidores Web com o mesmo conteúdo, criamos o balanceamento no varnish através dos directors. Os directors são grupos lógicos de backends, e dependendo do tipo de director, o varnish vai mandando as requisições para todos eles de modo balanceado. O tipo de director mais usado é o round-robin, que apenas vai distribuindo igualmente as requisições entre todos os servidores. No tutorial.vcl, a parte de nossos backends locais ficaria assim:

probe basico {
    .url = "/";
    .timeout = 1s;
    .window = 5;
    .threshold = 3;
    .initial = 3;
}

backend default {
  .host = "127.0.0.1";
  .port = "80";
  .probe = basico;
}

backend default_2 {
  .host = "127.0.0.1";
  .port = "90";
  .probe = basico;
}

backend default_3 {
  .host = "127.0.0.1";
  .port = "91";
  .probe = basico;
}

backend default_4 {
  .host = "127.0.0.1";
  .port = "92";
  .probe = basico;
}

# Requisições serão distribuídas igualmente em round-robin
director balanceamento round-robin {
  { .backend = default; }
  { .backend = default_2; }
  { .backend = default_3; }
  { .backend = default_4; }
}

E no vcl_recv, na primeira linha:

set req.backend = balanceamento;

Pronto, recarregue o VCL com o nome tutorial-5. Note que ao invés de eu definir as opções do probe em todos os backends, eu resolvi utilizar apenas uma definição de probe (nome basico), e referenciei em todos os backends. Isso ajudou a deixar o VCL menos redundante.

Agora todas as requisições serão distribuídas entre os 4 servidores. A linha do vcl_recv é obrigatória pois se você definir um backend no VCL mas não utilizá-lo em nenhum lugar, o Varnish reclama e não compila o VCL.

O que cachear e o que não cachear

Eu não sei se o verbo cachear realmente existe, mas se todo mundo fala não deve estar errado (hehehe). Agora que aprendemos a definir os backends e a mexer com o VCL, vem a parte mais importante (em minha opinião) do varnish: cachear ou não cachear? Nos exemplos anteriores, eu resolvi cachear todas as requisições GET, só para ficar melhor ilustrado. Mas não é essa a melhor prática!

A regra é a seguinte: cacheie apenas as páginas que não mudam de usuário para usuário.

Por exemplo, uma página de uma notícia ou uma lista, um menu, uma galeria de imagens, as próprias imagens, o CSS, o javascript, tudo isso pode ser cacheado. Mas se no site há uma página personalizada pro usuário, do tipo “Bem vindo, Walter Bishop“, ou dados de um carrinho de compras, ou a administração de algum site que requer autenticação de usuário e senha, esses não podem ser cacheados.

A razão deste raciocínio é simples: se você cachear o conteúdo personalizado de um usuário, outro usuário receberá esse conteúdo também. Cachear uma administração de um site, por exemplo, é uma péssima idéia, se um usuário faz login, os outros podem ver a administração como se fosse esse usuário, sem precisar fazer login também.

Outra regra importante de cache é: não é possível fazer cache de um método HTTP POST. Em teoria, o POST é utilizado para formulários e sua resposta é sempre individual, caindo então na regra anterior.

No vcl_recv do varnish, a função return nos diz o que fazer com a requisição depois de tratá-la. Se retornar lookup, o varnish tenta buscar aquela URL no cache. Se retornar pass, o varnish não olha no cache, vai direto pro backend. Nos exemplos anteriores, colocamos sempre o lookup como retorno para simplificar.

Vamos começar com uma regra para nunca olhar no cache a administração do WordPress, que fica em /wp-admin/. Para isso, eu coloco as seguintes linhas adicionais antes do lookup:

  if (req.url ~ "^/wp-admin/") {
    return (pass);
  }

  if (req.request == "GET") {
    return (lookup);
  }

O que eu quis dizer com as três primeiras linhas foi: caso a URL da requisição comece com /wp-admin/, o vcl_recv retornará pass, e não procurará no cache para o usuário. Simples não?

Note que eu, da mesma forma que antes, utilizei uma expressão regular para comparar a URL. Quando eu faço uma comparação com o til (~), posso usar expressões regulares, e quando eu uso dois iguais (==), significa que a string é literal, exata. Gaste uns minutinhos revendo o arquivo e veja as diferenças.

E aproveitando, no caso do WordPress há outras páginas totalmente dinâmicas que não estão no wp-admin, podemos utilizar então a seguinte expressão regular no lugar da anterior:

  if (req.url ~ "^/wp-(admin|login|cron)") {
    return (pass);
  }

Agora além do /wp-admin, o varnish não vai procurar um cache do /wp-login e do /wp-cron. Como expressões regulares saem um pouco do foco deste tutorial, se não souber procure outras referências para aprender o poder dessas expressões!

Recarregue o VCL com o nome de tutorial-5 e experimente agora testar com o wget e ver se o Age da página wp-login.php está aumentando:

wget -O /dev/null -S http://localhost:81/wp-login.php

Viu?

Agora vamos para o vcl_fetch. O vcl_fetch é quando o varnish vai até o backend buscar uma página, e é nessa função que você especifica realmente se vai fazer cache ou não. Mas como assim? Não já usamos o pass no vcl_recv?

A função vcl_fetch é sempre chamada quando o varnish busca a página no servidor. Quando você retorna um pass no vcl_recv, ele não vai lá buscar no backend? Então ele acaba passando pelo vcl_fetch sempre :-)

Assim como no vcl_recv, o vcl_fetch também tem dois retornos principais. O retorno deliver (atualmente usado no nosso exemplo) pega a resposta do backend e entrega (daí o nome) pro usuário. Já o retorno hit_for_pass força o varnish a não armazenar nenhuma cópia de cache da requisição e responder pro usuário uma requisição única, exatamente a que foi retornada pelo backend.

Em outras palavras, no nosso exemplo até agora o varnish está fazendo cache da administração do WordPress, mas não está entregando pro usuário. Se vários usuários resolverem entrar na administração ao mesmo tempo, o Varnish poderá usar algumas páginas de cache para não enfileirar as requisições. Então pra não desperdiçar espaço à toa no cache e já que queremos respostas sempre únicas, basta adicionar a mesma regra de antes, bem no começo do vcl_fetch:

  if (req.url ~ "^/wp-(admin|login|cron)") {
    return (hit_for_pass);
  }

(Repare que agora o pass virou hit_for_pass).

O vcl_fetch também serve para algo muito importante: definir o tempo de cache. Geralmente em um site, itens diferentes pedem tempos de cache diferentes. Por exemplo, a página principal pode ter um tempo de cache de 1 minuto, enquanto imagens, css e javascript podem ter 10 minutos.

No nosso exemplo atual, colocamos um tempo de cache de 30 segundos com a linha:

set beresp.ttl = 30s;

Basta usar as condições e testar as URLs, dando para cada tipo um tempo diferente. Coloque estas linhas no lugar da anterior:

  if (req.url == "/") {
    set beresp.ttl = 1m;
  } elsif (req.url ~ "\.(jpg|png|gif|css|js)(\?.*)?$") {
    set beresp.ttl = 10m;
  } else {
    set beresp.ttl = 30s;
  }

Recarregue o VCL com o nome tutorial-6 e teste a raíz do servidor com o wget. Veja que o Age vai até 60. Teste também as imagens, veja que vai até 600 (600 segundos = 10 minutos).

Agora que você já viu as funcionalidades principais do VCL, dê uma olhada de volta no default.vcl, que vem com o varnish e está todo comentado. Lembre-se que todas essas regras são padrões no varnish e são adicionadas à sua configuração. Tente identificar as regras por alguns minutos.

Veja se confere:

O padrão do vcl_recv é procurar no cache, a não ser que…

  • A requisição não seja do tipo GET, HEAD, PUT, POST, TRACE, OPTIONS, DELETE. O varnish só consegue entender estes tipos de requisição, e qualquer outra ele manda diretamente para o backend sem nenhum tratamento por parte do varnish (retorno “pipe”);
  • A requisição não for nem GET e nem HEAD. Na prática, são os únicos métodos que dá para cachear;
  • Existe autenticação HTTP ou Cookies na requisição. Se existir, quer dizer que a requisição é personalizada para cada usuário (mas nem sempre…).

O padrão do vcl_fetch é entregar imediatamente do cache, a não ser que…

  • O tempo de cache tenha expirado (TTL menor que zero);
  • O servidor configurou algum cookie (resposta personalizada);
  • Há uma variação de cache (Vary).

Como pode ver também, existem outras funções, mas não vamos nos aprofundar nela nesse tutorial.

Fluxo do VCL

Como vimos anteriormente, o Varnish segue um fluxo que começa na função vcl_recv. A partir dessa função, você pode tratar a requisição e passar para outras funções. Existe um fluxo bem definido de como as requisições passam por todo o Varnish. As imagens abaixo foram extraídas da documentação oficial do Varnish e correspondem à versão atual na data de escrita desse tutorial (3.0).

Fluxograma de Requisição Varnish Simples:

fluxograma varnish simples 190x300 Uma introdução ao Varnish

Fluxograma de Requisição Varnish Completo:

fluxograma varnish completo 148x300 Uma introdução ao Varnish

Durante o tutorial, usamos no exemplo algumas variáveis do VCL que permitiram comparar a URL, cabeçalhos HTTP, tempos de cache, entre outros. Existem dezenas dessas variáveis e elas podem ser usadas nas várias funções do VCL.

Olhando a imagem do fluxo completo, pode-se notar que do lado de cada nó de função (exemplos: vcl_recv(), vcl_fetch(), vcl_hit(), etc) há uma lista de quais variáveis podem ser usadas em cada função. Repare por exemplo, que as variáveis começando com beresp só podem ser usada no vcl_fetch.

Eis algumas das principais variáveis:

  • client.ip – IP de quem fez a requisição (o cliente)
  • req.* – Variáveis da requisição em si. Por ser os dados da requisição, ela existe desde o começo (vcl_recv) e persiste em praticamente todo o resto do fluxo.
  • req.http.* – Toda vez que a variável começar com req.http., significa um cabeçalho HTTP qualquer na requisição. Por exemplo, usamos o req.http.host para indicar o cabeçalho “Host” do HTTP, que indica qual domínio o usuário usou na barra de endereço de um navegador.
  • req.url – A URL da requisição, ou seja, tudo que vem depois do domínio, exemplo: /tutoriais/varnish-intro
  • req.http.cookies – Os cookies que o navegador manda para o servidor.
  • req.backend – Em qual backend a requisição vai buscar a página.
  • beresp.http.* – Parecido com o req.http.*, toda vez que a variável começar com beresp.http., significa um cabeçalho HTTP de resposta do servidor. Por exemplo, se um servidor enviou um cookie, este cookie estará em beresp.http.Set-Cookie.
  • beresp.status – Qual o código de status HTTP recebido pelo backend. 200, 301, 304, 404, 500, 503, e por aí vai. Com essa variável, pode-se por exemplo, no vcl_fetch definir um tempo de cache para páginas não-encontradas (404) e outro para páginas que retornam com sucesso (200).
  • beresp.ttl – Quanto tempo o objeto é válido dentro do cache e pode ser usado para entregar ao usuário. Pode-se usar notações como 10s (10 segundos), 10m (10 minutos), 1h (1 hora), e por aí vai.
  • obj.* – São variáveis que correspondem ao objeto armazenado dentro do cache. Um exemplo de uso dessa variável é saber quanto tempo falta para o cache expirar (obj.ttl).
  • resp.* – Antes de mandar a resposta para o usuário final, você pode usar essas variáveis no vcl_deliver para mexer no que vai ser enviado. Algo muito comum de se fazer é retirar alguns cabeçalhos HTTP como resp.http.Server, resp.http.X-Powered-By, e outros que o pessoal de segurança sempre pede para tirar :P

Uma lista completa das variáveis pode ser obtida através do guia de referência do VCL do Varnish (em inglês), disponível nesta URL..

Referências