实现高打击率¶
现在Varnish已经启动并运行,您可以通过Varnish访问您的Web应用程序了。除非您的应用程序是专门为在Web加速器后工作而编写的,否则您可能需要对配置或应用程序进行一些更改,以便在Varnish中获得高命中率。
Varnish不会缓存您的数据,除非它绝对确定这样做是安全的。因此,为了理解Varnish如何决定是否以及如何缓存页面,我们将通过几个工具来指导您理解Varnish设置中正在发生的事情。
请注意,您需要一个工具来查看在Varnish和后端之间飞行的HTTP头。在Varnish服务器上,完成此操作的最简单方法是使用 Varnishlog 和 Varnish 但有时客户端工具是有意义的。以下是我们常用的几种方法。
工具:varnishtop¶
您可以使用varnishtop来确定哪些URL对后端的访问最多。 varnishtop -i BereqURL
是一个基本命令,它向您显示Varnish向后端发送的最多的请求。您可以看到一些其他的例子 Varnish 用法在 统计数据 。
工具:varnishlog¶
当您确定了一个经常发送到后端的URL时,您可以使用 Varnishlog 看一看这份申请。 varnishlog -q 'ReqURL ~ "^/foo/bar"'
将向您显示来自客户端的请求匹配 /foo/bar 。
了解更多有关如何 Varnishlog 作品请参阅 登录Varnish 或者是手册页。
工具:LWP-请求¶
lwp-request 是一个工具,它是Perl的万维网库的一部分。它是几个非常基本的程序,可以执行HTTP请求并向您显示结果。我们主要使用这两个程序, GET
和 HEAD
。
Vg.no是第一个使用Varnish的网站,在那里运行Varnish的人非常有线索。因此,查看它们的HTTP头是很有趣的。让我们向他们的主页发送GET请求::
$ GET -H 'Host: www.vg.no' -Used http://vg.no/
GET http://vg.no/
Host: www.vg.no
User-Agent: lwp-request/5.834 libwww-perl/5.834
200 OK
Cache-Control: must-revalidate
Refresh: 600
Title: VG Nett - Forsiden - VG Nett
X-Age: 463
X-Cache: HIT
X-Rick-Would-Never: Let you down
X-VG-Jobb: http://www.finn.no/finn/job/fulltime/result?keyword=vg+multimedia Merk:HeaderNinja
X-VG-Korken: http://www.youtube.com/watch?v=Fcj8CnD5188
X-VG-WebCache: joanie
X-VG-WebServer: leon
好的。让我们来看看 GET
的确如此。 GET
通常发送HTTP0.9请求,该请求没有‘主机’标头。因此,我们添加了一个带有‘-H’选项的‘主机’标头。‘-U’打印请求标头,‘-S’打印响应状态,‘-e’打印响应标头,‘-d’丢弃实际内容。我们并不真正关心内容,只关心标题。
如您所见,VG在它们的报头中添加了相当多的信息。有些标题,比如“X-Rick-Will-Never”,是专门针对vg.no和他们有点奇怪的幽默感的。其他的,如‘X-VG-Webcache’是用于调试目的的。
因此,要检查站点是否为特定URL设置了Cookie,只需执行::
GET -Used http://example.com/ |grep ^Set-Cookie
工具:实时HTTP标头¶
还有一个名为Firefox的插件 Live HTTP Headers 。该插件可以向您显示正在发送和接收的报头。 Live HTTP Headers 可以在https://addons.mozilla.org/en-US/firefox/addon/3829/或通过谷歌搜索“Live HTTP Header”找到。
HTTP标头的作用¶
伴随着每个HTTP请求和响应的是一串携带元数据的头。Varnish将查看这些标头,以确定缓存内容是否合适,以及Varnish可以将内容缓存多长时间。
请注意,当Varnish考虑这些标头时,Varnish实际上认为自己 part of 实际的网络服务器。理由是两者都在你的控制之下。
这一术语 surrogate origin cache 并不是由IETF或RFC2616很好地定义的,所以Varnish的各种工作方式可能与您的预期不同。
让我们来看看你应该注意的重要标题:
缓存控制¶
“Cache-Control”标头指示缓存如何处理内容。Varnish关心的是 max-age 参数,并使用该参数计算对象的TTL。
因此,请确保发出带有max-age标头的“缓存控制”标头。您可以查看Varnish Software的Drupal服务器发出的命令:
$ GET -Used http://www.varnish-software.com/|grep ^Cache-Control
Cache-Control: public, max-age=600
年龄¶
Varnish添加了一个‘Age’标头来指示对象在Varnish中保留了多长时间。你可以把“Age”中的“Age”去掉 Varnishlog 使用 varnishlog -I RespHeader:^Age
。
普拉格玛¶
HTTP 1.0服务器可能会发送标头 Pragma: nocache
。VARNISH忽略此标头。您可以很容易地在VCL中添加对此标头的支持。
在……里面 vcl_backend_response **
if (beresp.http.Pragma ~ "nocache") {
set beresp.uncacheable = true;
set beresp.ttl = 120s; # how long not to cache this url.
}
覆盖生存时间(TTL)¶
有时,您的后端会行为不端。根据您的设置,覆盖Varnish中的TTL可能比修复有些麻烦的后端更容易。
您需要VCL来标识您想要的对象,然后将‘beresp.ttl’设置为您想要的任何内容::
sub vcl_backend_response {
if (bereq.url ~ "^/legacy_broken_cms/") {
set beresp.ttl = 5d;
}
}
本例将您站点上的旧遗留内容的TTL设置为5天。
强制对某些请求和某些响应进行缓存¶
由于您可能仍然有这个笨重的后端,使用起来不是很友好,所以您可能想要覆盖更多的Varnish内容。我们建议您尽可能多地依赖默认缓存规则。强制Varnish在缓存中查找对象非常容易,但实际上并不推荐这样做。
规范您的命名空间¶
有些站点是通过大量的主机名访问的。Http://www.varnish-software.com/,http://varnish-software.com/和http://varnishsoftware.com/都指向同一站点。因为Varnish不知道它们是相同的,所以Varnish将为每个主机名缓存每个页面的不同版本。您可以通过设置重定向或使用以下VCL在您的Web服务器配置中缓解此问题:
if (req.http.host ~ "(?i)^(www.)?varnish-?software.com") {
set req.http.host = "varnish-software.com";
}
Http变化¶
HTTP Vary is not a trivial concept. It is by far the most misunderstood HTTP header.
许多响应头告诉客户端有关正在交付的HTTP对象的一些信息。客户端可以根据自己的喜好请求不同的HTTP对象变体。他们的偏好可能包括编码或语言等内容。当客户更喜欢英国英语时,这通过 Accept-Language: en-uk
。缓存需要将这些不同的变量分开,这是通过HTTP响应头‘Variable’来完成的。
当后端服务器发出 Vary: Accept-Language
它告诉Varnish,它需要为来自客户端的每种不同的接受语言缓存一个单独的版本。
如果两个客户端分别表示他们接受语言“en-us,en-UK”和“da,de”,如果后端指示Varnish需要改变‘Accept-Language’头,则Varnish将缓存并提供页面的两个不同版本。
请注意,“不同”引用的标头需要匹配 exactly 才会有匹配的机会。因此,Varnish将保留一页的两份副本,如果其中一份是为“en-us,en-uk”创建的,另一份是为“en-us,en-uk”创建的。缺少空格将迫使Varnish缓存另一个版本。
因此,要在使用Variat的同时实现高命中率,关键是对后端不同的标头进行标准化。请记住,大小写的不同可能会强制不同的缓存条目。
下面的VCL代码将‘Accept-Language’标头标准化为“en”、“de”或“fr”,按优先顺序排列:
if (req.http.Accept-Language) {
if (req.http.Accept-Language ~ "en") {
set req.http.Accept-Language = "en";
} elsif (req.http.Accept-Language ~ "de") {
set req.http.Accept-Language = "de";
} elsif (req.http.Accept-Language ~ "fr") {
set req.http.Accept-Language = "fr";
} else {
# unknown language. Remove the accept-language header and
# use the backend default.
unset req.http.Accept-Language
}
}
不同的分析错误¶
如果Variable标头无法解析,或者Variable标头中列出的任何客户端标头超过65k个字符的限制,Varnish将返回“503内部服务器错误”页面。在这些情况下,会添加一个‘SLT_ERROR’日志条目。
陷阱-不同:用户-代理¶
某些应用程序或应用程序服务器发送 Vary: User-Agent
以及它们的内容。这会指示Varnish为每个版本的“用户代理”缓存一个单独的副本,而且数量很多。即使是同一浏览器的单个补丁级别,也会根据它们运行的操作系统生成至少10个不同的“用户代理”头文件。
所以如果你 really 需要根据用户代理的不同而有所不同。确保将头球标准化,否则你的命中率将受到严重影响。使用上面的代码作为模板。
缓存未命中¶
当Varnish在缓存中找不到请求的对象时,默认情况下,它会基于响应可能被缓存的假设从后端执行FETCH。这有两个重要的后果:
对同一对象的并发后端请求为 coalesced --一次只执行一个提取,其他挂起的提取等待结果(除非您已经实现了下面中所述的状态之一 无法缓存的内容 )。这是为了防止您的后端在缓存的响应过期时,或者如果它从一开始就没有被缓存,就会被“雷鸣般的牛群”击中。如果对第一次提取的响应被缓存,则该缓存对象可以立即传递给其他挂起的请求。
如果Varnish在缓存中没有要验证的对象,则缓存未命中的后端请求不能是有条件的;也就是说,它不能包含标头
If-Modified-Since
或If-None-Match
,这可能会导致后端返回没有响应正文的状态“304 Not Modify”。否则,可能没有对缓存的响应。如果这些标头出现在客户端请求中,则会从后端请求中删除它们。
通过为缓存的对象设置宽限时间(默认为10秒),您允许Varnish在等待合并的获取时提供过时的内容,合并的获取在过时的响应被发送到客户端时以异步方式运行。有关详情,请参阅 Grace模式和Keep 。
尽管在缓存未命中时,条件请求的标头会从后端获取中删除,但Varnish仍然可以使用“304 NOT MODIFIED”来响应客户端请求,如果结果响应允许的话。在传递时,如果客户端请求具有 If-None-Match
标头与 ETag
响应中的标头,或者如果 If-Modified-Since
请求标头等于或晚于 Last-Modified
响应头,Varnish将向客户端发送304响应。无论命中还是未命中,都会发生这种情况。
如果Varnish在缓存中有一个可以对其执行验证的对象,它可以向后端发送条件请求。通过设置,可以确保为此目的保留对象 beresp.keep
在……里面 vcl_backend_response
**
sub vcl_backend_response {
# Keep the response in cache for 4 hours if the response has
# validating headers.
if (beresp.http.ETag || beresp.http.Last-Modified) {
set beresp.keep = 4h;
}
}
过期对象在持续时间内不会从缓存中删除 beresp.keep
在其TTL和宽限期到期之后。这将增加缓存的存储需求,但如果您有空间,可能值得保留可以验证相当长时间的陈旧对象。如果后端可以在TTL过期很久之后发送304响应,则可以节省获取带宽并减少存储压力;如果不能,则它与任何其他缓存未命中没有什么不同。
但是,如果您希望后端获取不是有条件的,只需删除 vcl_backend_fetch
**
sub vcl_backend_fetch {
# To prevent conditional backend fetches.
unset bereq.http.If-None-Match;
unset bereq.http.If-Modified-Since;
}
这应该只有在条件获取对后端有问题时才是必要的,例如,如果评估响应是否不变对后端应用程序来说代价太高,或者如果响应只是有错误的。从Varnish的角度来看,304响应显然是更可取的;使用空响应正文进行提取节省了带宽,并且不必在缓存中分配存储,因为现有的缓存对象得到了重用。
总而言之,即使在缓存未命中的情况下,也可以通过以下方式提高性能:
确保缓存的对象具有宽限时间,在此期间,当在后台执行获取时,可以将过时的对象提供给客户端,以及
为缓存对象设置保持时间,这些缓存对象在过期后可以使用304响应进行验证。
无法缓存的内容¶
由于各种原因,某些回复无法缓存。内容可能是个性化的,具体取决于 Cookie
标头,或者它可能只是在每次请求时重新生成的那类东西。缓存对此无能为力,但您可以做出一些决定,帮助Varnish以最适合您的需求的方式处理不可缓存的响应。
需要考虑的问题包括:
阻止请求合并
同一对象的响应是否(以及多久)可能再次变为可缓存
不管你想不想传递
If-Modified-Since
和If-None-Match
从客户端请求到后端的标头,以允许后端以状态304响应
正在传递客户端请求¶
根据您的站点的工作方式,您可能能够识别客户端对无法缓存的响应的请求,例如,如果URL与某些模式匹配,或由于请求标头的内容。在这种情况下,可以将FETCH设置为 pass 使用 return(pass)
从… vcl_recv
**
sub vcl_recv {
if (req.url ~ "^/this/is/personal/") {
return(pass);
}
}
对于传球,不存在合并请求。由于PASS指示响应将不可缓存,因此等待可能被缓存的响应是没有意义的,并且对象的所有挂起的获取都是并发的。否则,等待最终被证明不可缓存的对象的提取可能会被序列化--挂起的提取将等待第一个提取,当结果没有输入到缓存中时,下一个提取开始,而所有其他提取都在等待,依此类推。
当请求被传递时,可以在 vcl_backend_*
子例程的事实是 bereq.uncacheable
和 beresp.uncachable
都是真的。后端响应将不会被缓存,即使它满足否则将允许它的条件,例如 Cache-Control
设置正TTL。
PASS是默认设置(即, builtin.vcl
打电话 return(pass)
在……里面 vcl_recv
)如果客户端请求满足以下条件:
请求方法是标准的HTTP/1.1方法,但不是
GET
或HEAD
要么有一个
Cookie
或者是一个Authorization
标头,指示响应可能是个性化的
如果要覆盖默认设置,例如,如果您确定即使存在Cookie,响应也是可缓存的,请确保 return
在可能通过您自己的路径选择的任何路径的末尾调用 vcl_recv
。但如果你这样做了,内置的 vcl_recv
被处决;所以仔细看看 vcl_recv
在……里面 builtin.vcl
,并复制您自己所需的任何部分 vcl_recv
。
与缓存命中和未命中一样,如果客户端请求头和响应头允许,Varnish决定在传递后向客户端发送304响应。这可能意味着,即使后端看到相同的请求头,Varnish也会向客户端发送304响应 (If-Modified-Since
和/或 If-None-Match
),但决定不以状态304进行响应,同时仍设置响应头 ETag
和/或 Last-Modified
因此,304似乎是有根据的。如果您不希望Varnish这样做,那么删除 vcl_pass
**
sub vcl_pass {
# To prevent 304 client responses after a pass.
unset req.http.If-None-Match;
unset req.http.If-Modified-Since;
}
命中未命中¶
您可能无法识别对中不可缓存内容的所有请求 vcl_recv
。您可能希望允许后端通过设置 Cache-Control
标头,但直到Varnish收到后端响应才能看到,因此 vcl_recv
不能知道这件事。
默认情况下,如果请求未通过并且后端响应不可缓存,则通过设置 beresp.uncacheable
至 true
在……里面 vcl_backend_response
。在高速缓存中保存一个最小的对象,以便在后续的查找中可以识别“命中未命中”状态。(缓存用于记住对象在有限的时间内是不可缓存的。)在这种情况下,不会执行请求合并,因此获取可以并发运行。否则,命中未命中的提取就像缓存未命中一样,这意味着:
响应可能在以后的请求中变得可缓存,例如,如果它使用
Cache-Control
,以及提取不能是有条件的,因此
If-Modified-Since
和If-None-Match
从后端请求中删除头部。
什么时候 beresp.uncacheable
设置为 true
,那么 beresp.ttl
确定命中未命中状态最多可以持续多长时间。命中未命中状态在这段时间过去之后结束,或者如果后端在它过去之前返回了可缓存的响应(经过 beresp.ttl
只是意味着最小缓存对象过期,就像任何其他缓存对象过期一样)。如果返回可缓存的响应,则该对象替换命中未命中对象,并且对它的后续请求将是缓存命中。如果之前未返回可缓存的响应 beresp.ttl
则对该对象的下一个请求将是普通的未命中,因此将受到请求合并的影响。
当Varnish看到它在新请求上命中了一个命中未命中对象时,它会执行 vcl_miss
因此,您为缓存未命中编写的任何自定义VCL也将适用于命中未命中的情况。
builtin.vcl
集合 beresp.uncacheable
至 true
在指示无法缓存响应的多个条件下,调用命中未命中状态,例如,如果TTL被计算为0或者如果存在 Set-Cookie
标题。 beresp.ttl
设置为两分钟,直到 builtin.vcl
在这种情况下,这就是默认情况下命中失误的持续时间。
您可以设置 beresp.uncacheable
如果您在其他情况下需要命中未命中:
sub vcl_backend_response {
if (beresp.http.X-This-Is == "personal") {
set beresp.uncacheable = true;
}
}
请注意,有一次 beresp.uncacheable
已设置为 true
不能将其设置回 false
;在VCL中尝试执行此操作将被忽略。
尽管后端获取从来不是命中不命中的条件,但如果客户端请求头和响应头,Varnish可以决定(像在所有其他情况下一样)向客户端发送304响应 ETag
或 Last-Modified
允许它。如果要防止出现这种情况,请删除中的if-客户端请求头 ``vcl_miss`` *
sub vcl_miss {
# To prevent 304 client responses on hit-for-miss.
unset req.http.If-None-Match;
unset req.http.If-Modified-Since;
}
击出一次传球¶
命中未命中的后果是后端获取不能是有条件的,因为命中未命中允许后续响应可缓存。对于非常大且不可缓存的响应,这可能是有问题的,但可能会用304响应进行验证。例如,您可能希望客户端每次都通过后端验证对象,仅在对象更改时发送响应。
对于这种情况,可以使用以下命令将对象设置为“命中传球” return(pass(DURATION))
从… vcl_backend_response
,其中持续时间确定击球传球状态持续的时间::
sub vcl_backend_response {
# Set hit-for-pass for two minutes if TTL is 0 and response headers
# allow for validation.
if (beresp.ttl <= 0s && (beresp.http.ETag || beresp.http.Last-Modified)) {
return(pass(120s));
}
}
与命中未命中一样,最小对象被输入到缓存中,以便在后续请求中识别命中传递状态。然后,该请求被作为PASS处理,就像 vcl_recv
传回了传球。这意味着没有请求合并,并且 If-Modified-Since
和 If-None-Match
客户端请求中的报头被传递到后端,从而后端响应可以是304。
Varnish可以执行 vcl_pass
当它击中一个击球传球对象时。因此,您可以在VCL中使用相同的代码安排自己的传球和击球处理。
如果您想要阻止Varnish向后端发送条件请求,那么在 vcl_backend_fetch
,如上面针对高速缓存未命中所示。如果您希望阻止Varnish在交付时根据客户端请求和响应头决定向客户端发送304响应,则从 vcl_pass
,如上图所示的PASS。
当“命中传球TTL”在 return
声明已过。与传递一样,对命中传递获取的响应永远不会被缓存,即使它本来可以满足可缓存的条件。因此,与命中未命中不同,不可能通过可缓存的响应提前结束命中传球状态。在“命中传球TTL”过去之后,对该对象的下一个请求被当作普通的未命中来处理。
可以通过设置来结束缓存对象的命中传递状态 req.hash_always_miss
至 true
在……里面 vcl_recv
对于将命中对象的请求(您必须编写实现该对象的VCL)。发生这种情况的请求被强制为缓存未命中,之后对象的状态取决于后端响应的处理--它可能变成缓存命中、命中未命中,或者可能再次被设置为命中换传递。
命中未命中是对不可缓存内容的默认处理。不属于 builtin.vcl
调用一击即传,因此如果需要它,您必须添加必要的 return
对您自己的VCL的声明。