2013-07-16 29 views
5

我不知道我在做什麼。我有很多問題,我不知道從哪裏開始。 這裏是我的配置:Symfony ESI在啓用Cookies時打破清漆緩存

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

我第一次在其作爲反向代理,而不是清漆app.php文件禁用的Symfony AppCache

這裏是我的光油配置:

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

因爲:

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

光油消除公共頁面的所有Cookies和命中緩存,這是很好的,但... 由於Varnish刪除了所有無法管理保持登錄狀態的cookie(沒有會話cookie表示沒有會話)。

我有一個沒有緩存頭的ESI塊的登錄按鈕,但這仍然不能解決它。 這裏是前端控制器:

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

模板擴展layout.html.twig

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

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

    ... 
</body> 

和控制器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; 
} 

我不知道如何管理會話cookie爲Symfony。因爲我在每個頁面上都有一個Facebook連接按鈕,所以我需要用戶會話。而且由於Symfony甚至爲匿名用戶創建了一個cookie,所以我對所有請求都有會話cookie。

幫助將不勝感激:)

感謝, 馬克西姆


UPDATE:新的VCL文件後,建議:

... 

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

每一個公共頁面,這就是緩存正確。我在登錄頁面的任何地方都有緩存HIT,但現在不是什麼大問題。

我遇到的問題是在標題中的ESI塊。我可以在apache訪問日誌中看到清漆請求<esi:include>調用/_fragment url。

在主頁上呈現的ESI塊不正確(它顯示登錄網址,就好像用戶沒有登錄一樣),但是如果我直接調用/_fragment,則返回的塊是正確的(包含用戶信息)。

我真的不知道它的來自哪裏,但我如此接近:)

回答

5

從入站請求刪除Cookie頭從所有導致ESI包括請求刪除。既然你要訪問的餅乾頭中包含的資源,而不是父母,這是緩存,試試這個:

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

這條從父頁面請求的瀏覽器的cookie,但它重新添加到由esi產生的esi請求:在返回的頁面中包含標籤。我沒有粘上面的代碼,所以它可能不是100%完美。

更新

在vcl_recv,如果你再也不想緩存或遞歸ESI處理的ESI片段,您可以更改第二個if塊:

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

感謝隊友。由於Symfony即使對匿名用戶也創建會話cookie,您認爲爲登錄用戶創建第二個cookie可能是解決方案嗎?如果'logged_in' Cookie存在不會取消設置cookie,否則取消設置Cookie。這會導致匿名用戶登錄用戶的Cache MISS和Cache HIT。你怎麼看? – maxwell2022

+1

檢查清漆請求的cookie標頭,確保您的VCL正確設置它。當直接敲擊/ _fragment資源時,請檢查響應頭以確保它們顯式不可緩存(或者如我在更新中所建議的那樣爲esi請求返回(傳遞)) –