AskOverflow.Dev

AskOverflow.Dev Logo AskOverflow.Dev Logo

AskOverflow.Dev Navigation

  • Início
  • system&network
  • Ubuntu
  • Unix
  • DBA
  • Computer
  • Coding
  • LangChain

Mobile menu

Close
  • Início
  • system&network
    • Recentes
    • Highest score
    • tags
  • Ubuntu
    • Recentes
    • Highest score
    • tags
  • Unix
    • Recentes
    • tags
  • DBA
    • Recentes
    • tags
  • Computer
    • Recentes
    • tags
  • Coding
    • Recentes
    • tags
Início / server / Perguntas / 1153078
Accepted
Xowap
Xowap
Asked: 2024-02-07 19:14:06 +0800 CST2024-02-07 19:14:06 +0800 CST 2024-02-07 19:14:06 +0800 CST

Revalidação de conteúdo baseada em ETag

  • 772

Meu CMS gera páginas bastante complexas e, portanto, demora um pouco para fazê-lo (cerca de 2 segundos), o que está bem acima do meu orçamento de tempo para fornecer páginas ao cliente.

Porém é muito barato para mim saber a versão atual de uma determinada página e é muito fácil para mim dizer se uma determinada versão ainda está atualizada. Como tal, eu gostaria de poder colocar uma estratégia baseada em ETag onde cada solicitação para uma página precise ser revalidada, mas o servidor responderá em no máximo 10 ms se o conteúdo não mudar.

Para que isso seja eficaz, preciso compartilhar esse cache entre todos os meus clientes. Enquanto a ETag for revalidada, todas as minhas páginas permanecerão idênticas para todos os usuários, para que eu possa compartilhar seu conteúdo com segurança.

Para fazer isso, minha página emite um:

Cache-Control: public, no-cache, must-revalidate
ETag: W/"xxx"

Ao testar em um navegador, funciona muito bem: a página permanece no cache e simplesmente é revalidada no servidor toda vez que atualizo a página, obtendo 304 na maioria das vezes ou 200 quando altero a versão do conteúdo.

Tudo que preciso agora é compartilhar esse cache entre clientes. Essencialmente:

  1. Fase A
    1. O cliente A envia uma solicitação ao proxy
    2. O proxy não tem cache, então pergunta ao back-end
    3. Backend responde 200 com uma ETag
    4. O proxy responde 200 com uma ETag
  2. Fase B
    1. O cliente B envia a mesma solicitação ao proxy
    2. O proxy está em cache, mas deve revalidar (porque não há cache, deve-revalidar e ETag)
    3. O back-end responde com 304 (porque a solicitação de revalidação inclui o cabeçalho If-None-Match com a ETag armazenada em cache)
    4. Proxy responde 200 com um Etag
  3. Fase C
    1. O cliente A envia a mesma solicitação novamente, desta vez com If-None-Match
    2. O proxy solicita ao back-end o cabeçalho If-None-Match fornecido (não o armazenado em cache)
    3. O servidor back-end responde 304
    4. O proxy responde 304

Eu tentei o nginx, mas requer muitos ajustes para fazê-lo funcionar remotamente. Então experimentei o Traefik antes de perceber que o middleware de cache faz parte da versão corporativa. Então percebi que o Varnish parece implementar o que eu quero.

Então aqui vou eu com minha configuração do Varnish:

vcl 4.0;

backend default {
    .host = "localhost";
    .port = "3000";
}

backend api {
    .host = "localhost";
    .port = "8000";
}

sub vcl_recv {
    if (req.url ~ "^/back/" || req.url ~ "^/_/") {
        set req.backend_hint = api;
    } else {
        set req.backend_hint = default;
    }
}

E claro... Não funcionou.

Ao variar os Cache-Controlcabeçalhos, obtenho o resultado de um cache compartilhado, mas que não é revalidado, ou apenas uma passagem para o cliente, mas nunca parece manter o conteúdo em cache como gostaria.

O que estou faltando para implementar essa lógica de revalidação de cache/ETag compartilhada? Suponho que estou perdendo algo óbvio, mas não consigo descobrir o quê.

http
  • 1 1 respostas
  • 69 Views

1 respostas

  • Voted
  1. Best Answer
    Thijs Feryn
    2024-02-07T20:17:23+08:002024-02-07T20:17:23+08:00

    Definir um TTL

    Conforme declarado na VCL integrada : Varnish trata a no-cachediretiva da mesma maneira que private& no-store: o conteúdo não irá parar no cache.

    Para a revalidação baseada em ETag isso representa um problema, porque não há nada para comparar.

    Meu conselho seria definir um TTL baixo para garantir que ele acabe no cache. Eu recomendaria usar o seguinte Cache-Controlcabeçalho:

    Cache-Control: public, s-maxage=3, must-revalidate
    

    Faça com que o Varnish entenda que é necessário revalidar

    Outra questão é que o Varnish não entende a must-revalidatediretiva, embora a apoie stale-while-revalidate.

    Adicionar must-revalidatetem o mesmo efeito que stale-while-revalidate=0: não podemos servir conteúdo obsoleto quando o objeto expira e exigir validação síncrona imediata.

    Isso define o gracetemporizador interno para zero.

    Porém, com o seguinte código VCL, você pode fazer com que o Varnish respeite a must-revalidatediretiva:

    sub vcl_backend_response {
        if(beresp.http.Cache-Control ~ "must-revalidate") {
            set beresp.grace = 0s;
        }
    }
    

    Aumente o tempo de manutenção

    Ao definir o valor de carência como zero e atribuir um TTL baixo, os objetos expirarão rapidamente e não permanecerão disponíveis por tempo suficiente para revalidação.

    Conforme explicado em https://stackoverflow.com/questions/68177623/varnish-default-grace-behavior/68207764#68207764 , o Varnish possui um conjunto de temporizadores que usa para decidir sobre expiração, revalidação e obsolescência tolerada.

    Minha sugestão seria aumentar o keeptimer no VCL. Esse cronômetro garante que objetos expirados e fora de uso sejam mantidos, sem correr o risco de veicular conteúdo desatualizado.

    A única razão pela qual o keepcronômetro existe é para revalidação de ETag, então é exatamente disso que você precisa.

    Sugiro usar a seguinte VCL para suportar must-revalidatee aumentar o keepcronômetro:

    sub vcl_backend_response {
        set beresp.keep = 1d;
        if(beresp.http.Cache-Control ~ "must-revalidate") {
            set beresp.grace = 0s;
        }
    }
    

    Esse snippet aumenta o keepcronômetro para um dia, permitindo que o conteúdo expirado permaneça no cache por um dia enquanto ocorre a revalidação.

    Cuidado com cookies (e cabeçalhos de autorização)

    Apesar de ter todos os recursos necessários para oferecer suporte à revalidação de ETag e armazenar objetos no cache, é importante que as solicitações relevantes não ignorem o cache. Isso não tem nada a ver com Cache-Controlcabeçalhos, mas com cabeçalhos de solicitação.

    Se você der uma olhada na VCL integrada para a vcl_recvsub-rotina , notará que, por padrão, o Varnish ignora o cache para solicitações que contêm um Authorizationcabeçalho ou Cookiecabeçalho.

    Se o seu site usa cookies, leia o tutorial a seguir para saber como remover cookies de rastreamento que podem prejudicar sua taxa de acertos: https://www.varnish-software.com/developers/tutorials/removing-cookies-varnish/

    Caso de teste de verniz como prova de conceito

    Armazene o seguinte conteúdo VTC etag.vtce execute varnishtest etag.vtcpara validar o caso de teste:

    varnishtest "Testing ETag & 304 status codes in Varnish"
    
    server s1 {
        # First backend request is not a conditional one
        # Return 200 with the ETag
        rxreq
        expect req.method == "GET"
        expect req.http.If-None-Match == <undef>
        txresp -status 200 \
         -hdr "Cache-Control: public, s-maxage=3, must-revalidate" \
         -hdr "ETag: abc123" -bodylen 7
    
    
        # Second backend request is a conditional one with a matching ETag
        # Return a 304
        rxreq
        expect req.method == "GET"
        expect req.http.If-None-Match == "abc123"
        txresp -status 304 \
         -hdr "Cache-Control: public, s-maxage=3, must-revalidate" \
         -hdr "ETag: abc123"
    
        # Third backend request is a conditional where the Etag doesn't match
        # The content has been updated
        # Return a 200 with  the updated content and the new Etag
        rxreq
        expect req.method == "GET"
        expect req.http.If-None-Match == "abc123"
        txresp -status 200 \
         -hdr "Cache-Control: public, s-maxage=3, must-revalidate" \
         -hdr "ETag: xyz456" -bodylen 8
    
    } -start
    
    varnish v1 -vcl+backend {
        sub vcl_backend_response {
            set beresp.keep = 1d;
            if(beresp.http.Cache-Control ~ "must-revalidate") {
                set beresp.grace = 0s;
            }
        }
    } -start
    
    client c1 {
        # Send a regular request
        # Expect a cache miss
        # Trigger the first backend response
        # Return 200 with a response body
        txreq -url "/"
        rxresp
        expect resp.status == 200
        expect resp.http.Age == "0"
        expect resp.http.ETag == "abc123"
        expect resp.http.Cache-Control == "public, s-maxage=3, must-revalidate"
        expect resp.bodylen == 7
    
        # Wait for 1 second
        delay 1
    
        # Send a conditional request with an If-None-Match header
        # Should be a cache hit
        # Should return a 304 without a response body
        # Varnish is responsible for this 304
        txreq -url "/" -hdr "If-None-Match: abc123"
        rxresp
        expect resp.status == 304
        expect resp.http.Age != "0"
        expect resp.http.ETag == "abc123"
        expect resp.http.Cache-Control == "public, s-maxage=3, must-revalidate"
        expect resp.bodylen == 0
    
        # Wait for 3 seconds
        delay 3
    
        # Send a conditional request with an If-None-Match header
        # Should be a cache miss
        # Trigger the second backend response
        # Should return a 304 without a response body
        # The server is responsible for this 304
        txreq -url "/" -hdr "If-None-Match: abc123"
        rxresp
        expect resp.status == 304
        expect resp.http.Age == "0"
        expect resp.http.ETag == "abc123"
        expect resp.http.Cache-Control == "public, s-maxage=3, must-revalidate"
        expect resp.bodylen == 0
    
        # Wait for 4 seconds
        delay 4
    
        # Send a conditional request with an If-None-Match header
        # Should be a cache miss
        # Trigger the third backend response
        # Should return a 200 with a response body
        # Content has been updated: a new Etag is returned
        txreq -url "/" -hdr "If-None-Match: abc123"
        rxresp
        expect resp.status == 200
        expect resp.http.Age == "0"
        expect resp.http.ETag == "xyz456"
        expect resp.http.Cache-Control == "public, s-maxage=3, must-revalidate"
        expect resp.bodylen == 8
    
    } -run
    

    Conclusão

    Se você seguir as etapas que descrevi, sua configuração do Varnish suportará a revalidação de ETag de duas maneiras:

    • Do cliente para o objeto armazenado em cache no Varnish enquanto o objeto ainda está atualizado
    • Do Varnish para o servidor de origem quando o objeto expirar, mas enquanto ainda estiver mantido no cache devido ao keepcronômetro

    Considere também aumentar o TTL do cache se o seu conteúdo permitir. Tudo isso depende de quantas páginas/recursos mudam.

    Por favor, perceba também que manter os objetos por mais tempo (devido a um keepvalor aumentado) irá preencher o cache. Quando o cache está cheio, uma estratégia LRU é aplicada para remover o objeto menos popular do cache.

    Se set beresp.keep = 1d;for muito e o Varnish começar a remover objetos do cache porque está cheio, considere diminuir o keepvalor.

    • 2

relate perguntas

  • IIS 7.5 (Windows 7) - Erro HTTP 401.3 - Não autorizado

Sidebar

Stats

  • Perguntas 205573
  • respostas 270741
  • best respostas 135370
  • utilizador 68524
  • Highest score
  • respostas
  • Marko Smith

    Você pode passar usuário/passar para autenticação básica HTTP em parâmetros de URL?

    • 5 respostas
  • Marko Smith

    Ping uma porta específica

    • 18 respostas
  • Marko Smith

    Verifique se a porta está aberta ou fechada em um servidor Linux?

    • 7 respostas
  • Marko Smith

    Como automatizar o login SSH com senha?

    • 10 respostas
  • Marko Smith

    Como posso dizer ao Git para Windows onde encontrar minha chave RSA privada?

    • 30 respostas
  • Marko Smith

    Qual é o nome de usuário/senha de superusuário padrão para postgres após uma nova instalação?

    • 5 respostas
  • Marko Smith

    Qual porta o SFTP usa?

    • 6 respostas
  • Marko Smith

    Linha de comando para listar usuários em um grupo do Windows Active Directory?

    • 9 respostas
  • Marko Smith

    O que é um arquivo Pem e como ele difere de outros formatos de arquivo de chave gerada pelo OpenSSL?

    • 3 respostas
  • Marko Smith

    Como determinar se uma variável bash está vazia?

    • 15 respostas
  • Martin Hope
    Davie Ping uma porta específica 2009-10-09 01:57:50 +0800 CST
  • Martin Hope
    kernel O scp pode copiar diretórios recursivamente? 2011-04-29 20:24:45 +0800 CST
  • Martin Hope
    Robert ssh retorna "Proprietário incorreto ou permissões em ~/.ssh/config" 2011-03-30 10:15:48 +0800 CST
  • Martin Hope
    Eonil Como automatizar o login SSH com senha? 2011-03-02 03:07:12 +0800 CST
  • Martin Hope
    gunwin Como lidar com um servidor comprometido? 2011-01-03 13:31:27 +0800 CST
  • Martin Hope
    Tom Feiner Como posso classificar a saída du -h por tamanho 2009-02-26 05:42:42 +0800 CST
  • Martin Hope
    Noah Goodrich O que é um arquivo Pem e como ele difere de outros formatos de arquivo de chave gerada pelo OpenSSL? 2009-05-19 18:24:42 +0800 CST
  • Martin Hope
    Brent Como determinar se uma variável bash está vazia? 2009-05-13 09:54:48 +0800 CST

Hot tag

linux nginx windows networking ubuntu domain-name-system amazon-web-services active-directory apache-2.4 ssh

Explore

  • Início
  • Perguntas
    • Recentes
    • Highest score
  • tag
  • help

Footer

AskOverflow.Dev

About Us

  • About Us
  • Contact Us

Legal Stuff

  • Privacy Policy

Language

  • Pt
  • Server
  • Unix

© 2023 AskOverflow.DEV All Rights Reserve