我有一个 Ruby on Rails 应用程序,前端是 varnish+nginx。由于除非您是登录用户,否则大多数网站内容都是静态的,因此我想在用户注销时使用 varnish 大量缓存网站,但在用户登录时仅缓存静态资产。
当用户登录时,他们的 Cookie:标头中会出现 cookie“user_credentials”,此外,我需要跳过 /login 和 /sessions 上的缓存,以便用户可以首先获取他们的“user_credentials”cookie。
Rails 默认不设置缓存友好的 Cache-control 标头,但是我的应用程序在用户未登录时设置“public,s-max-age=60”标头。Nginx 设置为返回所有静态资产的“远期”过期标头。
我目前的配置是登录时完全绕过所有内容的缓存,包括静态资产 — — 并在退出时返回所有内容的缓存 MISS。我花了几个小时绕来绕去,这是我当前的 default.vcl
director rails_director round-robin {
{
.backend = {
.host = "xxx.xxx.xxx.xxx";
.port = "http";
.probe = {
.url = "/lbcheck/lbuptest";
.timeout = 0.3 s;
.window = 8;
.threshold = 3;
}
}
}
}
sub vcl_recv {
if (req.url ~ "^/login") {
pipe;
}
if (req.url ~ "^/sessions") {
pipe;
}
# The regex used here matches the standard rails cache buster urls
# e.g. /images/an-image.png?1234567
if (req.url ~ "\.(css|js|jpg|jpeg|gif|ico|png)\??\d*$") {
unset req.http.cookie;
lookup;
} else {
if (req.http.cookie ~ "user_credentials") {
pipe;
}
}
# Only cache GET and HEAD requests
if (req.request != "GET" && req.request != "HEAD") {
pipe;
}
}
sub vcl_fetch {
if (req.url ~ "^/login") {
pass;
}
if (req.url ~ "^/sessions") {
pass;
}
if (req.http.cookie ~ "user_credentials") {
pass;
} else {
unset req.http.Set-Cookie;
}
# cache CSS and JS files
if (req.url ~ "\.(css|js|jpg|jpeg|gif|ico|png)\??\d*$") {
unset req.http.Set-Cookie;
}
if (obj.status >=400 && obj.status <500) {
error 404 "File not found";
}
if (obj.status >=500 && obj.status <600) {
error 503 "File is Temporarily Unavailable";
}
}
sub vcl_deliver {
if (obj.hits > 0) {
set resp.http.X-Cache = "HIT";
} else {
set resp.http.X-Cache = "MISS";
}
}
答案1
好的,最后我设法使用以下 vcl 文件解决了这个问题。请注意,我添加了一些额外的位,以便在后端死机时允许缓存过期宽限。
看来我的主要失败是unset req.http.Set-Cookie;
在应该使用unset obj.http.Set-Cookie;
该vcl_fetch
部分时使用了它。(obj
在 vcl_fetch 和req
vcl_recv 部分)。
director rails_director round-robin {
{
.backend = {
.host = "xxx.xxx.xxx.xxx";
.port = "http";
.probe = {
.url = "/lbcheck/lbuptest";
.timeout = 0.3 s;
.window = 8;
.threshold = 3;
}
}
}
}
sub vcl_recv {
if (req.backend.healthy) {
set req.grace = 30s;
} else {
set req.grace = 1h;
}
if (req.url ~ "^/login") {
pipe;
}
if (req.url ~ "^/sessions") {
pipe;
}
if (req.url ~ "\.(css|js|jpg|jpeg|gif|ico|png)\??\d*$") {
unset req.http.cookie;
lookup;
} else {
if (req.http.cookie ~ "user_credentials") {
pipe;
} else {
unset req.http.cookie;
}
}
# Only cache GET and HEAD requests
if (req.request != "GET" && req.request != "HEAD") {
pipe;
}
}
sub vcl_fetch {
set obj.grace = 1h;
if (req.url ~ "^/login") {
pass;
}
if (req.url ~ "^/sessions") {
pass;
}
if (req.http.cookie ~ "user_credentials") {
pass;
} else {
unset obj.http.Set-Cookie;
}
# cache CSS and JS files
if (req.url ~ "\.(css|js|jpg|jpeg|gif|ico|png)\??\d*$") {
unset obj.http.Set-Cookie;
}
if (obj.status >=400 && obj.status <500) {
error 404 "File not found";
}
if (obj.status >=500 && obj.status <600) {
error 503 "File is Temporarily Unavailable";
}
}
sub vcl_deliver {
if (obj.hits > 0) {
set resp.http.X-Cache = "HIT";
} else {
set resp.http.X-Cache = "MISS";
}
}
答案2
我无法发表评论因此我将其作为答案发布。
注意:从 2.1.0 开始,obj.* 在 vcl_fetch 中被称为 beresp.*,并且 obj.* 现在是只读的。
这些似乎也最好放在 vcl_recv 而不是 vcl_fetch 中:
if (req.url ~ "^/login") {
pipe;
}
if (req.url ~ "^/sessions") {
pipe;
}
最后,在你的后端定义中,我会添加
.max_connections = 32;
调整您允许 nginx/apache 创建的 Passenger 后端数量。如果您不设置此限制,则应密切关注 Passenger 全局队列(假设您正在使用 Passenger)。