2013-07-16 20 views
5

Non so più cosa sto facendo. Ho avuto così tanti problemi che non so da dove cominciare. Ecco la mia configurazione:Symfony ESI interrompe la cache di Varnish se i cookie sono abilitati

varnishd (varnish-3.0.3 revision 9e6a70f) 
Server version: Apache/2.2.22 (Unix) 
Symfony 2.3.1 

Prima ho disabilitato Symfony AppCache nel file app.php che è stato utilizzato come proxy inverso, invece di vernice.

Ecco la mia configurazione Varnish:

Varnish (80) <--> Apache (8080)

# /etc/varnish/default.vcl 
backend default { 
    .host = "127.0.0.1"; 
    .port = "8080"; 
} 

sub vcl_recv { 
    if (req.http.Cache-Control ~ "no-cache") { 
     return (pass); 
    } 

    if (!(req.url ~ "^/dashboard/")) { 
     unset req.http.Cookie; 
    } 

    # Remove has_js and Google Analytics __* cookies. 
    set req.http.Cookie = regsuball(req.http.Cookie, "(^|;\s*)(_[_a-z]+|has_js)=[^;]*", ""); 
    # Remove a ";" prefix, if present. 
    set req.http.Cookie = regsub(req.http.Cookie, "^;\s*", ""); 

    #set req.http.Cookie = regsuball(req.http.Cookie, "(^|;\s*)(__[a-z]+|has_js)=[^;]*", ""); 
    set req.http.Surrogate-Capability = "abc=ESI/1.0"; 

    if (req.restarts == 0) { 
     if (req.http.x-forwarded-for) { 
      set req.http.X-Forwarded-For = 
      req.http.X-Forwarded-For + ", " + client.ip; 
     } else { 
      set req.http.X-Forwarded-For = client.ip; 
     } 
    } 

    if (req.request != "GET" && 
     req.request != "HEAD" && 
     req.request != "PUT" && 
     req.request != "POST" && 
     req.request != "TRACE" && 
     req.request != "OPTIONS" && 
     req.request != "DELETE") { 
     /* Non-RFC2616 or CONNECT which is weird. */ 
     return (pipe); 
    } 

f (req.request != "GET" && req.request != "HEAD") { 
     /* We only deal with GET and HEAD by default */ 
     return (pass); 
    } 

    if (req.http.Authorization) { 
     /* Not cacheable by default */ 
     return (pass); 
    } 

    return (lookup); 
} 

sub vcl_pipe { 
    return (pipe); 
} 

sub vcl_pass { 
    return (pass); 
} 

sub vcl_hash { 
    hash_data(req.url); 

    if (req.http.host) { 
     hash_data(req.http.host); 
    } else { 
     hash_data(server.ip); 
    } 

    return (hash); 
} 

sub vcl_fetch { 
    if (beresp.http.Surrogate-Control ~ "ESI/1.0") { 
     unset beresp.http.Surrogate-Control; 
     set beresp.do_esi = true; 
    } 

    # Varnish determined the object was not cacheable 
    if (beresp.ttl <= 0s) { 
     set beresp.http.X-Varnish-Cacheable = "NO:Not Cacheable"; 

    # You don't wish to cache content for logged in users 
    } elsif (req.http.Cookie ~ "(UserID|_session)") { 
     set beresp.http.X-Varnish-Cacheable = "NO:Got Session"; 
     return(hit_for_pass); 

    # You are respecting the Cache-Control=private header from the backend 
    } elsif (beresp.http.Cache-Control ~ "private") { 
     set beresp.http.X-Varnish-Cacheable = "NO:Cache-Control=private"; 
     return(hit_for_pass); 

    # Varnish determined the object was cacheable 
    } else { 
     set beresp.http.X-Varnish-Cacheable = "YES"; 
    } 

    if (beresp.status >= 300) { 
     return (hit_for_pass); 
    } 

    if (beresp.http.Pragma ~ "no-cache" || 
     beresp.http.Cache-Control ~ "no-cache" || 
     beresp.http.Cache-Control ~ "private") { 
     return (hit_for_pass); 
    } 

    return (deliver); 
} 

sub vcl_hit { 
    return (deliver); 
} 

sub vcl_deliver { 
    if (obj.hits > 0) { 
     set resp.http.X-Varnish-Cached = "HIT"; 
    } else { 
     set resp.http.X-Varnish-Cached = "MISS"; 
    } 
    return (deliver); 
} 

sub vcl_error { 
    set obj.http.Content-Type = "text/html; charset=utf-8"; 
    set obj.http.Retry-After = "5"; 

    synthetic {" 
<?xml version="1.0" encoding="utf-8"?> 
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" 
    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> 
<html> 
    <head> 
    <title>"} + obj.status + " " + obj.response + {"</title> 
    </head> 
    <body> 
    <h1>Error "} + obj.status + " " + obj.response + {"</h1> 
    <p>"} + obj.response + {"</p> 
    <h3>Guru Meditation:</h3> 
    <p>XID: "} + req.xid + {"</p> 
    <hr> 
    <p>Varnish cache server</p> 
    </body> 
</html> 
"}; 

    return (deliver); 
} 

sub vcl_init { 
    return (ok); 
} 

sub vcl_fini { 
    return (ok); 
} 

A causa di:

if (!(req.url ~ "^/dashboard/")) { 
    unset req.http.Cookie; 
} 

Varnish rimuove tutti i cookie delle pagine pubbliche e colpisce la cache, che è buono, ma ... Dal momento che Varnish cancella tutti i cookie non riesco a mantenere il login (nessun cookie di sessione significa nessuna sessione).

Ho il pulsante di accesso in un blocco ESI con intestazione no-cache ma questo ancora non lo aggiusta. Ecco il controller frontend:

public function indexAction() 
{ 
    $response = $this->render('AcmeCoreBundle:Default:index.html.twig'); 
    $response->setSharedMaxAge(21600); // 6 hours 
    return $response; 
} 

Il modello estende la layout.html.twig:

<html lang="en-US"> 
<head> 
    ... 
</head> 
<body> 
    ... 

    <div class="top-right"> 
     {{ render_esi(controller('AcmeUserBundle:Default:loginBox')) }} 
    </div> 

    ... 
</body> 

e il controller AcmeUserBundle:Default:loginBox:

public function loginBoxAction() 
{ 
    $response = $this->render('AcmeUserBundle:Block:home_login.html.twig'); 
    $response->setVary('Cookies', false); 
    $response->setMaxAge(0); 
    $response->setPrivate(); 

    return $response; 
} 

Non so come gestire il cookie di sessione per Symfony. Perché ho un pulsante di connessione di Facebook su ogni pagina ho bisogno di avere la sessione utente. E poiché Symfony sta creando un cookie anche per utenti anonimi, ho cookie di sessione su tutte le richieste.

aiuto sarebbe molto apprezzato :)

Grazie, Maxime


UPDATE: Nuovi file VCL dopo le raccomandazioni:

... 

sub vcl_recv { 

    if (!(req.url ~ "^/dashboard") && !(req.url ~ "^/logout") && !(req.url ~ "^/_fragment") && req.esi_level == 0) { 
     set req.http.Esi-Cookie = req.http.Cookie; 
     unset req.http.Cookie; 
    } 

    if (!(req.url ~ "^/dashboard") && req.esi_level > 0) { 
     set req.http.Cookie = req.http.Esi-Cookie; 
    } 

    # Remove has_js and Google Analytics __* cookies. 
    set req.http.Cookie = regsuball(req.http.Cookie, "(^|;\s*)(_[_a-z]+|has_js)=[^;]*", ""); 

    # Remove a ";" prefix, if present. 
    set req.http.Cookie = regsub(req.http.Cookie, "^;\s*", ""); 

    # Force ESI capability header 
    set req.http.Surrogate-Capability = "abc=ESI/1.0"; 

    if (req.restarts == 0) { 
     if (req.http.x-forwarded-for) { 
      set req.http.X-Forwarded-For = 
      req.http.X-Forwarded-For + ", " + client.ip; 
     } else { 
      set req.http.X-Forwarded-For = client.ip; 
     } 
    } 

    if (req.request != "GET" && 
     req.request != "HEAD" && 
     req.request != "PUT" && 
     req.request != "POST" && 
     req.request != "TRACE" && 
     req.request != "OPTIONS" && 
     req.request != "DELETE") { 
     /* Non-RFC2616 or CONNECT which is weird. */ 
     return (pipe); 
    } 

    # if Authorization or no-cache header we skip the cache 
    if (req.http.Authorization || req.http.Cache-Control ~ "no-cache") { 
     return (pass); 
    } 

    # If not GET or HEAD request we skip the cache 
    if (req.request != "GET" && req.request != "HEAD") { 
     return (pass); 
    } 

    return (lookup); 
} 

... 

sub vcl_fetch { 

    if (!(req.url ~ "^/dashboard") && !(req.url ~ "^/login_check")) { 
     unset beresp.http.set-cookie; 
    } 

    if (beresp.http.Surrogate-Control ~ "ESI/1.0") { 
     unset beresp.http.Surrogate-Control; 
     set beresp.do_esi = true; 
    } 

    # Varnish determined the object was not cacheable 
    if (beresp.ttl <= 0s) { 
     set beresp.http.X-Varnish-Cacheable = "NO:Not Cacheable"; 

    # You don't wish to cache content for logged in users 
    } elsif (req.http.Cookie ~ "(UserID|_session)") { 
     set beresp.http.X-Varnish-Cacheable = "NO:Got Session"; 
     return(hit_for_pass); 

    # You are respecting the Cache-Control=private header from the backend 
    } elsif (beresp.http.Cache-Control ~ "private") { 
     set beresp.http.X-Varnish-Cacheable = "NO:Cache-Control=private"; 
     return(hit_for_pass); 

    # Varnish determined the object was cacheable 
    } else { 
     set beresp.http.X-Varnish-Cacheable = "YES"; 
    } 

    if (beresp.status >= 300) { 
     return (hit_for_pass); 
    } 

    if (beresp.http.Pragma ~ "no-cache" || 
     beresp.http.Cache-Control ~ "no-cache" || 
     beresp.http.Cache-Control ~ "private") { 
     return (hit_for_pass); 
    } 

    return (deliver); 
} 

Ogni pagina pubblica è ora nella cache correttamente . Ho cache HIT ovunque tranne nella pagina di accesso, ma per ora non è un grosso problema.

Il problema che ho è con il blocco ESI nell'intestazione. Riesco a vedere nel log di accesso di Apache che la vernice richiede lo <esi:include> chiamando l'URL /_fragment.

Il blocco ESI visualizzato nella home page non è corretto (visualizza URL di accesso come se l'utente non ha effettuato l'accesso) tuttavia se si chiama direttamente lo /_fragment, il blocco restituito è corretto (con le informazioni dell'utente).

Io in realtà non so da dove è venuta da ma io sono così vicino :)

risposta

5

Rimozione del Cookie da una richiesta in entrata rimozione da tutti i risultante ESI comprendono le richieste.Dal momento che si vuole accedere al intestazione nel risorse inclusi, ma non il genitore, che è memorizzato nella cache, provate questo:

if (!(req.url ~ "^/dashboard/") && req.esi_level == 0) { 
    set req.http.Esi-Cookie = req.http.Cookie; 
    unset req.http.Cookie; 
} 
if (!(req.url ~ "^/dashboard/") && req.esi_level > 0) { 
    set req.http.Cookie = req.http.Esi-Cookie; 
} 

Questo elimina il cookie del browser dalla richiesta per la pagina padre, ma ri-aggiunge al richieste esi risultanti da esi: include tag nella pagina restituita. Non ho filtrato il codice sopra, quindi potrebbe non essere perfetto al 100%.

Aggiornamento

In vcl_recv, se non si vuole mai mettere in cache o ricorsivamente esi elaborare un frammento esi, è possibile modificare il secondo blocco if a:

if (!(req.url ~ "^/dashboard/") && req.esi_level > 0) { 
    set req.http.Cookie = req.http.Esi-Cookie; 
    return (pass); 
} 
+1

Grazie amico. Poiché Symfony sta creando un cookie di sessione anche per gli utenti anonimi, pensi che la creazione di un secondo cookie per gli utenti registrati possa essere la soluzione? Se il cookie 'logged_in' presente non disattiva il cookie, altrimenti disabilita i cookie. Ciò comporterebbe Cache MISS per gli utenti registrati e Cache HIT per gli utenti anonimi. Cosa ne pensi? – maxwell2022

+1

Ispeziona l'intestazione del cookie delle richieste inviate alla vernice per assicurarti che il tuo VCL lo imposti correttamente. Inoltre, quando si colpisce direttamente la risorsa/_fragment, controllare le intestazioni di risposta per assicurarsi che siano esplicitamente non memorizzabili (o restituire (passare) per le richieste esi come suggerisco nell'aggiornamento) –

Problemi correlati