2013-07-16 58 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请求返回(传递)) –