![重复缓存页面:Varnish](https://linux22.com/image/602533/%E9%87%8D%E5%A4%8D%E7%BC%93%E5%AD%98%E9%A1%B5%E9%9D%A2%EF%BC%9AVarnish.png)
最近我们在服务器上配置了 Varnish,安装成功,但我们注意到,如果我们在多个浏览器中打开任何页面,无论页面是否缓存,Varnish 都会向 Apache 发送请求。如果我们在每个浏览器上刷新两次,它会创建同一页面的重复副本。
到底应该发生什么:
如果任何页面被 Varnish 缓存,当我们在浏览器中打开同一页面或从不同的 IP 地址打开该页面时,后续请求应该由 Varnish 本身提供。
以下是我的 default.vcl 文件
backend default {
.host = "127.0.0.1";
.port = "80";
}
sub vcl_recv {
if( req.url ~ "^/search/.*$")
{
}else {
set req.url = regsub(req.url, "\?.*", "");
}
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.backend.healthy) {
unset req.http.Cookie;
}
set req.grace = 6h;
if (req.url ~ "^/status\.php$" ||
req.url ~ "^/update\.php$" ||
req.url ~ "^/admin$" ||
req.url ~ "^/admin/.*$" ||
req.url ~ "^/flag/.*$" ||
req.url ~ "^.*/ajax/.*$" ||
req.url ~ "^.*/ahah/.*$") {
return (pass);
}
if (req.url ~ "(?i)\.(pdf|asc|dat|txt|doc|xls|ppt|tgz|csv|png|gif|jpeg|jpg|ico|swf|css|js)(\?.*)?$") {
unset req.http.Cookie;
}
if (req.http.Cookie) {
set req.http.Cookie = ";" + req.http.Cookie;
set req.http.Cookie = regsuball(req.http.Cookie, "; +", ";");
set req.http.Cookie = regsuball(req.http.Cookie, ";(SESS[a-z0-9]+|SSESS[a-z0-9]+|NO_CACHE)=", "; \1=");
set req.http.Cookie = regsuball(req.http.Cookie, ";[^ ][^;]*", "");
set req.http.Cookie = regsuball(req.http.Cookie, "^[; ]+|[; ]+$", "");
if (req.http.Cookie == "") {
unset req.http.Cookie;
}
else {
return (pass);
}
}
if (req.request != "GET" && req.request != "HEAD" &&
req.request != "PUT" && req.request != "POST" &&
req.request != "TRACE" && req.request != "OPTIONS" &&
req.request != "DELETE")
{return(pipe);} /* Non-RFC2616 or CONNECT which is weird. */
if (req.request != "GET" && req.request != "HEAD") {
return (pass);
}
if (req.http.Accept-Encoding) {
if (req.url ~ "\.(jpg|png|gif|gz|tgz|bz2|tbz|mp3|ogg)$") {
# No point in compressing these
remove req.http.Accept-Encoding;
} else if (req.http.Accept-Encoding ~ "gzip") {
set req.http.Accept-Encoding = "gzip";
} else if (req.http.Accept-Encoding ~ "deflate") {
set req.http.Accept-Encoding = "deflate";
} else {
# unknown algorithm
remove req.http.Accept-Encoding;
}
}
return (lookup);
}
sub vcl_deliver {
if (obj.hits > 0) {
set resp.http.X-Varnish-Cache = "HIT";
}
else {
set resp.http.X-Varnish-Cache = "MISS";
}
}
sub vcl_fetch {
if (beresp.status == 404 || beresp.status == 301 || beresp.status == 500) {
set beresp.ttl = 10m;
}
if (req.url ~ "(?i)\.(pdf|asc|dat|txt|doc|xls|ppt|tgz|csv|png|gif|jpeg|jpg|ico|swf|css|js)(\?.*)?$") {
unset beresp.http.set-cookie;
}
set beresp.grace = 6h;
}
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_pipe {
set req.http.connection = "close";
}
sub vcl_hit {
if (req.request == "PURGE")
{ban_url(req.url);
error 200 "Purged";}
if (!obj.ttl > 0s)
{return(pass);}
}
sub vcl_miss {
if (req.request == "PURGE")
{error 200 "Not in cache";}
}
解决方案
陷阱 - 变化:用户代理
某些应用程序或应用程序服务器会随其内容一起发送 Vary: User-Agent。这会指示 Varnish 为存在的每种 User-Agent 变体缓存单独的副本。User-Agent 变体有很多。即使是同一浏览器的单个补丁级别也会根据其运行的操作系统生成至少 10 个不同的 User-Agent 标头。
因此,如果您确实需要根据 User-Agent 进行更改,请务必规范化标头,否则您的命中率将受到严重影响。使用上述代码作为模板。
https://www.varnish-cache.org/docs/3.0/tutorial/vary.html#tutorial-vary
解决方法
一个解决方法是执行我们所说的“User-Agent-Washing”,其中 Varnish 将 Useragent 重写为后端真正关心的几种不同变体,如下所示:
sub vcl_recv {
if (req.http.user-agent ~ "MSIE") {
set req.http.user-agent = "MSIE";
} else {
set req.http.user-agent = "Mozilla";
}
}
答案1
首先,Varnish 不可能缓存一个 URL 的 2 个副本。
现在,我不确定命中/未命中检查,但是当我需要检查时,我会在 Firefox 中执行此操作并使用 Firebug。
我会打开firebug并打开网站。
其中,它将显示所获取的每个页面/图像的年龄,如附图所示。
如果年龄随着时间的推移而增加,那么对于我来说,Varnish 的效果相当不错。
据我所见,它在您的网站上运行良好。
答案2
这帮助我解决了这个问题:
取消注释注释掉或删除构成vch_hash
函数的行并重新启动 varnish。vcl_hash
用于创建特定缓存,例如针对用户、会话或特定 IP 地址。如果您希望页面从缓存(缓存后)提供,则可以取消 vcl_hash 函数。
先在测试环境中进行测试,以防万一。
嗨嗨。
编辑(澄清)
选项1:
通过在每行开头添加“#”符号来注释掉这些行。因此这些行
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_hash {
# hash_data(req.url);
# if (req.http.host) {
# hash_data(req.http.host);
# } else {
# hash_data(server.ip);
# }
# return (hash);
#}
以 a 开头的行将 #
被 varnish 忽略。
选项 2:
或者,您也可以将上述代码全部删除。最终结果是一样的。