如何在Varnish中失效URL且保留旧版本至新版本缓存完成
Varnish:标记URL失效但保留旧缓存直到更新完成的最佳实践
核心思路
你的需求本质是**"stale-while-revalidate"模式:标记缓存条目过时,但仍返回旧内容,同时后台异步从后端拉取新版本更新缓存。PURGE会直接删除缓存,不符合需求,应使用BAN**机制配合grace/stale配置实现。
具体实现步骤
1. 配置BAN规则替代PURGE
BAN不会删除缓存条目,只是标记匹配的条目为"过时"。后续请求时Varnish会优先返回旧缓存,同时触发后台刷新。
在vcl_recv中添加BAN处理逻辑
sub vcl_recv { # 处理BAN请求(建议通过IP白名单限制调用权限) if (req.method == "BAN") { # 替换allowed_ban_ips为你的授权IP列表 if (!client.ip ~ allowed_ban_ips) { return (synth(403, "Forbidden")); } # 通过自定义Header传递要标记的URL,支持模糊匹配(示例为精确匹配) ban("obj.http.x-cache-url == " + req.http.X-Ban-Target); return (synth(200, "Ban scheduled")); } }
在vcl_backend_response中添加缓存标识与grace配置
给缓存对象打标记,方便BAN精准匹配,同时配置grace时间确保旧缓存可被返回:
sub vcl_backend_response { # 给缓存对象绑定URL标识,用于BAN匹配 set beresp.http.x-cache-url = bereq.url; # 设置grace时间,根据业务调整(示例为1小时) set beresp.grace = 1h; # 可选:开启stale-if-error,后端出错时也返回旧缓存 set beresp.stale_if_error = 1h; }
2. 启用后台自动刷新
确保Varnish在返回旧缓存的同时,异步去后端拉取新版本:
sub vcl_hit { if (obj.ttl <= 0 || obj.http.X-Banned) { # 返回旧缓存,同时触发后端刷新 return (deliver_stale); } } sub vcl_deliver { # 给响应添加缓存状态标识,方便调试 if (obj.hits > 0) { set resp.http.X-Cache = "HIT"; } else { set resp.http.X-Cache = "MISS"; } # 删除自定义标识头,避免暴露给用户 unset resp.http.x-cache-url; }
3. 调用BAN的示例命令
# 标记指定URL为过时 curl -X BAN -H "X-Ban-Target:/product/123" http://your-varnish-host
关键注意事项
- 权限控制:必须限制BAN请求的来源IP,避免恶意滥用导致缓存雪崩。
- grace时间配置:要根据后端服务的更新频率和业务容忍度设置,过短可能导致频繁回源,过长可能延迟新版本上线。
- 避免PURGE:PURGE会直接删除缓存条目,导致后续请求必须等待后端响应,无法实现"先返回旧缓存再更新"的需求。
内容的提问来源于stack exchange,提问作者frlan
相关产品推荐
相关产品推荐

