由于该网站的性能缓慢,我开始将 info Varnish 视为缓存解决方案,并对 Google Analytics 有一些疑问。
当站点上有5K活跃用户时(根据GA的实时流量报告),后端服务器上的服务器负载飙升至30-40+,乘客队列开始堆积,站点几乎无法使用。我知道需要获得更好性能的缓慢查询和数据库工作,但目前我没有资源来优化查询和数据库架构、索引等,因此正在考虑添加 Varnish 。
我创建了一个图表来更好地显示堆栈,这是当前堆栈的样子:(该站点当前在 CDN 中缓存图像/css/js - Akamai)
我想在后端服务器前端添加两个 varnish 实例来缓存文章,堆栈将如下所示:
该站点是一个新闻站点,我正在寻找有关如何正确处理 cookie 和缓存的建议。对于第一阶段,我想简单地完全排除经过身份验证的用户并提供动态内容,因为同时经过身份验证的用户并不多。
混淆在于 Google Analytic 的 cookie。我的理解是,Google 使用 javascript 在客户端上设置 cookie,并且客户端直接与 Google 通信,因此后端不需要客户端发送的 GA cookie,并且在 vcl_recv 子例程中取消设置它们是安全的。
sub vcl_recv {
// 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*", "");
}
问题
由于默认情况下 varnish 不会缓存任何设置了 cookie 的内容,那么通过添加删除 GA cookie 的策略来实现上述堆栈是否安全?我知道如果没有微调 VCL 策略,我不会获得高命中率,但是在我的测试中,似乎即使在后端服务器前端使用默认 Varnish ,也有 30% 的命中率,在分析这些之后,我明白了大多数是 js/css 和图像文件,所以很明显,其中一些静态文件不是由 Akamai 甚至 Apache 提供的,而是传递给Passenger/Rails 以提供静态文件。这绝对需要纠正。
我是 Varnish 的新手,因此非常感谢我对 Varnish 或堆栈的任何其他细节/建议。
对于第 2 阶段 +
由于内容得到更新,我计划在两个 Varnish 服务器上执行清除,当应用更改时由后端服务器触发,例如用户评论、页面浏览量等。
有许多未更新的已归档文章,将它们永久缓存是否安全?
由于我打算使用 RAM 进行 Varnish 存储,我是否应该有额外的(第三个) Varnish ,并使用磁盘进行存储,明确地用于那些存档页面。也许在 Varnish 服务器的前面添加nginx堆栈,以将流量定向到已归档内容的特定 Varnish 实例?
负载均衡器-> Nginx 反向代理对> Varnish 对->(varnish LB 到 8 个后端服务器)
我也很感谢有关该体系结构的任何建议。如果您需要更多详细信息以提供更好的建议,请告诉我,我很乐意为您提供更多详细信息。
最佳答案
这是很多问题! :-)
Q. Is this a safe approach?
表面上,我会这么说。
通常,在流量大且内容变化快的新闻网站上设置 Varnish 可能是一项挑战。
一个非常好的检查方法是构建一个单独的 varnish 框并让它直接访问您的集群(而不是通过负载均衡器),并为其提供一个临时的公共(public) IP 地址。这将使您有机会针对 VCL 更改进行测试。您将能够测试评论、登录(如果有的话)以及其他任何事情,以确保没有意外。
Q. Will Google still track properly, including repeat visitors?
是的。 cookie 仅用于客户端。
您应该注意的一件事是,当后端发送 cookie 时,Varnish 也不会缓存内容。您将需要删除 vcl_fetch 上不需要的任何 cookie。如果使用 cookie 来跟踪用户状态,这可能是一个问题。
Q. Is there anything else that I need to watch for in my policies for phase1?
您需要在 Rails 中禁用机架缓存,并设置您自己的 header 。请注意,如果您删除 Varnish ,Rails 将在没有缓存的情况下运行,并且可能会崩溃!
这是我在我的 production.rb 中的内容:
# We do not use Rack::Cache but rely on Varnish instead
config.middleware.delete Rack::Cache
# varnish does not support etags or conditional gets
# to the backend (which is this app) so remove them too
config.middleware.delete Rack::ETag
config.middleware.delete Rack::ConditionalGet
在我的 application_controller 中,我有这个私有(private)方法:
def set_public_cache_control(duration)
if current_user
response.headers["Cache-Control"] = "max-age=0, private, must-revalidate"
else
expires_in duration, :public => true
response.headers["Expires"] = CGI.rfc1123_date(Time.now + duration)
end
end
这在我的其他 Controller 中被调用,以便我可以非常精细地控制将多少chacheing应用于站点的各个部分。我在作为 before_filter 运行的每个 Controller 中使用设置方法:
def setup
set_public_cache_control 10.minutes
end
(application_controller 有过滤器和空白设置方法,所以它可以在其他 Controller 中可选)
如果您有不需要 cookie 的站点部分,您可以根据 VCL 中的 URL 将它们剥离,并应用标题。
您可以像这样在 apache 配置中为静态 Assets 设置缓存时间(假设您使用的是默认 Assets 路径):
<LocationMatch "^/assets/.*$">
Header unset ETag
FileETag None
# RFC says only cache for 1 year
ExpiresActive On
ExpiresDefault "access plus 1 year"
Header append Cache-Control "public"
</LocationMatch>
<LocationMatch "^/favicon\.(ico|png)$">
Header unset ETag
FileETag None
ExpiresActive On
ExpiresDefault "access plus 1 day"
Header append Cache-Control "public"
</LocationMatch>
<LocationMatch "^/robots.txt$">
Header unset ETag
FileETag None
ExpiresActive On
ExpiresDefault "access plus 1 hour"
Header append Cache-Control "public"
</LocationMatch>
这些 header 将发送到您的 CDN,这将缓存 Assets 更长时间。观察 Varnish ,您仍然会看到请求以下降的速度进入。
我还会在页面不需要 cookie 但经常更改的所有内容上设置非常短的缓存。就我而言,我为主页设置了 10 秒的缓存时间。这对 Varnish 意味着一个用户请求将每 10 秒发送到后端。
您还应该考虑将 varnish 设置为使用宽限模式。这样一来,它就可以优先处理缓存中的陈旧内容,而不是让访问者从后端对刚刚过期的项目进行缓慢的响应。
Q. There are plenty of archived articles that don't get updated, is it safe to cache them forever?
为此,您需要更改您的应用程序,以便为那些已存档的文章发送不同的标题。这假设他们不会有 cookie。根据我在我的网站上所做的,我会这样做:-
在上面的设置中添加一个条件来更改缓存时间:
def setup
# check if it is old. This code could be anything
if news.last_updated_at < 1.months.ago
set_public_cache_control 1.year
else
set_public_cache_control 10.minutes
end
end
这会设置一个公共(public) header ,因此 Varnish 将缓存它(如果没有 cookie),任何远程缓存(在 ISP 或公司网关处)也会缓存它。
问题在于您是否想删除故事或更新它(例如,出于法律原因)。
在这种情况下,您应该向 Varnish 发送一个私有(private) header 以更改该 URL 的 TTL,但为其他所有人发送一个较短的公共(public) header 。
这将允许您将 Varnish 设置为(例如)1 年提供内容,同时它会发送 header 以告诉客户每 10 分钟返回一次。
在这些情况下,您需要添加一个机制来清除 Varnish 。
为了让您开始,我在 application_controller 中有第二种方法:
def set_private_cache_control(duration=5.seconds)
# logged in users never have cached content so no TTL allowed
if ! current_user
# This header MUST be a string or the app will crash
if duration
response.headers["X-Varnish-TTL"] = duration.to_s
end
end
end
在我的 vcl_fetch 我有这个:
call set_varnish_ttl_from_header;
vcl 函数是这样的:
sub set_varnish_ttl_from_header {
if (beresp.http.X-Varnish-TTL) {
C{
char *x_end = 0;
const char *x_hdr_val = VRT_GetHdr(sp, HDR_BERESP, "\016X-Varnish-TTL:"); /* "\016" is length of header plus colon in octal */
if (x_hdr_val) {
long x_cache_ttl = strtol(x_hdr_val, &x_end, 0);
if (ERANGE != errno && x_end != x_hdr_val && x_cache_ttl >= 0 && x_cache_ttl < INT_MAX) {
VRT_l_beresp_ttl(sp, (x_cache_ttl * 1));
}
}
}C
remove beresp.http.X-Varnish-TTL;
}
}
这样一来, header 就不会传递给(任何s-max-age)任何上游缓存。
设置方法如下所示:
def setup
# check if it is old. This code could be anything
if news.last_updated_at < 1.months.ago
set_public_cache_control 10.minutes
set_private_cache_control 1.year
else
set_public_cache_control 10.minutes
end
end
随时提出任何补充问题,我将更新此答案!
关于linux - 如何处理 Varnish 堆栈中的 Cookie,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/16095288/