缓存失效 #
一、缓存失效概述 #
1.1 为什么需要缓存失效 #
- 内容更新后需要及时反映
- 避免展示过期内容
- 保持数据一致性
- 支持即时发布
1.2 失效方法对比 #
| 方法 | 说明 | 适用场景 |
|---|---|---|
| PURGE | 立即删除特定URL缓存 | 精确失效 |
| BAN | 标记匹配的缓存失效 | 批量失效 |
| TTL过期 | 自动过期 | 自动失效 |
| Grace过期 | 宽限期后失效 | 优雅降级 |
二、PURGE方法 #
2.1 基本配置 #
vcl
acl purge {
"localhost";
"192.168.1.0"/24;
}
sub vcl_recv {
if (req.method == "PURGE") {
if (!client.ip ~ purge) {
return (synth(405, "Not allowed."));
}
return (purge);
}
}
2.2 使用PURGE #
bash
# PURGE特定URL
curl -X PURGE http://varnish:6081/images/logo.png
# PURGE带Host
curl -X PURGE -H "Host: example.com" http://varnish:6081/page.html
# PURGE带Cookie
curl -X PURGE -H "Cookie: session=abc" http://varnish:6081/page.html
2.3 PURGE响应处理 #
vcl
sub vcl_synth {
if (resp.status == 200 && req.method == "PURGE") {
set resp.http.Content-Type = "application/json";
synthetic({"{"status":"ok","message":"Purged"}"});
return (deliver);
}
}
2.4 PURGE日志 #
bash
# 查看PURGE日志
varnishlog -q "ReqMethod eq PURGE"
# 统计PURGE次数
varnishstat -1 -f MAIN.n_purges
三、BAN方法 #
3.1 基本配置 #
vcl
acl purge {
"localhost";
"192.168.1.0"/24;
}
sub vcl_recv {
if (req.method == "BAN") {
if (!client.ip ~ purge) {
return (synth(405, "Not allowed."));
}
ban("req.url ~ " + req.url);
return (synth(200, "Banned."));
}
}
3.2 BAN规则语法 #
vcl
# BAN特定URL
ban("req.url == /index.html");
# BAN URL模式
ban("req.url ~ ^/images/");
# BAN特定Host
ban("req.http.Host == example.com");
# BAN特定内容类型
ban("obj.http.Content-Type ~ image/");
# BAN组合条件
ban("req.http.Host == example.com && req.url ~ ^/news/");
# BAN特定标签
ban("obj.http.Surrogate-Key ~ article_123");
3.3 使用BAN #
bash
# BAN所有图片
curl -X BAN http://varnish:6081/ -H "X-Ban-Url: \.(jpg|png|gif)$"
# BAN特定路径
curl -X BAN http://varnish:6081/ -H "X-Ban-Url: ^/news/"
# BAN特定域名
curl -X BAN http://varnish:6081/ -H "X-Ban-Host: example.com"
3.4 高级BAN配置 #
vcl
sub vcl_recv {
if (req.method == "BAN") {
if (!client.ip ~ purge) {
return (synth(405, "Not allowed."));
}
# 基于请求头BAN
if (req.http.X-Ban-Url) {
ban("req.url ~ " + req.http.X-Ban-Url);
}
if (req.http.X-Ban-Host) {
ban("req.http.Host == " + req.http.X-Ban-Host);
}
if (req.http.X-Ban-Type) {
ban("obj.http.Content-Type ~ " + req.http.X-Ban-Type);
}
if (req.http.X-Ban-Tag) {
ban("obj.http.Surrogate-Key ~ " + req.http.X-Ban-Tag);
}
# 默认BAN请求URL
if (!req.http.X-Ban-Url &&
!req.http.X-Ban-Host &&
!req.http.X-Ban-Type &&
!req.http.X-Ban-Tag) {
ban("req.url ~ " + req.url);
}
return (synth(200, "Banned."));
}
}
3.5 BAN列表管理 #
bash
# 查看BAN列表
varnishadm ban.list
# 输出示例
Present bans:
1648123456.789012 C
1648123455.123456 C req.url ~ ^/images/
1648123454.987654 req.url ~ \.css$
# C表示已完成(completed)
3.6 BAN清理 #
bash
# 设置BAN清理参数
varnishadm param.set ban_lurker_age 60
varnishadm param.set ban_lurker_batch 1000
varnishadm param.set ban_lurker_sleep 0.01
四、PURGE vs BAN #
4.1 工作原理对比 #
text
PURGE:
┌─────────────────────────────────────────┐
│ Request ──► 查找缓存 ──► 立即删除 │
└─────────────────────────────────────────┘
BAN:
┌─────────────────────────────────────────┐
│ Request ──► 添加BAN规则 ──► 标记失效 │
│ │
│ 后续请求 ──► 检查BAN ──► 返回MISS │
└─────────────────────────────────────────┘
4.2 特性对比 #
| 特性 | PURGE | BAN |
|---|---|---|
| 作用范围 | 单个URL | URL模式 |
| 执行方式 | 立即删除 | 标记失效 |
| 性能影响 | 低 | 可能累积 |
| 精确度 | 高 | 模式匹配 |
| 使用场景 | 精确失效 | 批量失效 |
4.3 选择建议 #
| 场景 | 推荐方法 |
|---|---|
| 单个页面更新 | PURGE |
| 批量图片更新 | BAN |
| 全站更新 | BAN all |
| 特定内容类型 | BAN + Content-Type |
| 标签关联内容 | BAN + Surrogate-Key |
五、Surrogate-Key失效 #
5.1 设置Surrogate-Key #
vcl
sub vcl_backend_response {
# 基于URL设置标签
if (bereq.url ~ "^/articles/") {
set beresp.http.Surrogate-Key = "articles";
}
if (bereq.url ~ "^/products/") {
set beresp.http.Surrogate-Key = "products";
}
# 设置多个标签
if (bereq.url ~ "^/articles/([0-9]+)") {
set beresp.http.Surrogate-Key = "articles article_" +
re.group.1;
}
}
5.2 基于标签失效 #
vcl
sub vcl_recv {
if (req.method == "BAN") {
if (!client.ip ~ purge) {
return (synth(405, "Not allowed."));
}
if (req.http.X-Ban-Tag) {
ban("obj.http.Surrogate-Key ~ " + req.http.X-Ban-Tag);
return (synth(200, "Banned by tag."));
}
}
}
5.3 使用标签失效 #
bash
# 失效所有文章
curl -X BAN -H "X-Ban-Tag: articles" http://varnish:6081/
# 失效特定文章
curl -X BAN -H "X-Ban-Tag: article_123" http://varnish:6081/
# 失效多个标签
curl -X BAN -H "X-Ban-Tag: articles|products" http://varnish:6081/
六、主动失效 #
6.1 Webhook触发失效 #
vcl
sub vcl_recv {
if (req.url == "/webhook/purge" && req.method == "POST") {
if (!client.ip ~ purge) {
return (synth(405, "Not allowed."));
}
# 从请求体获取URL
# 需要使用VMOD处理请求体
return (synth(200, "Webhook received."));
}
}
6.2 CMS集成失效 #
php
<?php
// WordPress/Drupal等CMS集成示例
function purge_varnish_cache($url) {
$varnish_host = 'varnish.example.com';
$varnish_port = 6081;
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, "http://{$varnish_host}:{$varnish_port}{$url}");
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, "PURGE");
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_exec($ch);
curl_close($ch);
}
// 发布文章后失效缓存
purge_varnish_cache('/articles/new-article');
6.3 自动失效脚本 #
bash
#!/bin/bash
# auto_purge.sh
# 监控文件变化并触发失效
inotifywait -m -r -e modify,create,delete /var/www/html/ |
while read path action file; do
echo "File $file $action, purging cache..."
# 获取URL路径
url_path="${path#/var/www/html}$file"
# 发送PURGE请求
curl -X PURGE "http://localhost:6081$url_path"
done
七、批量失效 #
7.1 批量PURGE脚本 #
bash
#!/bin/bash
# batch_purge.sh
URLS=(
"/index.html"
"/about.html"
"/contact.html"
"/products/"
)
for url in "${URLS[@]}"; do
echo "Purging: $url"
curl -X PURGE "http://localhost:6081$url"
done
echo "Batch purge completed."
7.2 从站点地图批量失效 #
bash
#!/bin/bash
# sitemap_purge.sh
SITEMAP_URL="https://example.com/sitemap.xml"
# 获取sitemap中的URL
urls=$(curl -s "$SITEMAP_URL" | grep -oP '(?<=<loc>)[^<]+')
# 批量PURGE
for url in $urls; do
# 提取路径
path=$(echo "$url" | sed 's|https\?://[^/]*/||')
echo "Purging: /$path"
curl -X PURGE "http://localhost:6081/$path"
done
echo "Sitemap purge completed."
7.3 正则批量失效 #
bash
#!/bin/bash
# regex_ban.sh
# BAN所有图片
curl -X BAN -H "X-Ban-Url: \.(jpg|png|gif)$" http://localhost:6081/
# BAN所有CSS/JS
curl -X BAN -H "X-Ban-Url: \.(css|js)$" http://localhost:6081/
# BAN特定路径
curl -X BAN -H "X-Ban-Url: ^/news/2023/" http://localhost:6081/
八、失效监控 #
8.1 失效统计 #
bash
# 查看PURGE次数
varnishstat -1 -f MAIN.n_purges
# 查看BAN数量
varnishstat -1 -f MAIN.bans
# 查看BAN相关统计
varnishstat -1 -f MAIN.bans_*
8.2 失效日志 #
bash
# 查看PURGE日志
varnishlog -q "ReqMethod eq PURGE"
# 查看BAN日志
varnishlog -q "ReqMethod eq BAN"
# 记录失效日志
varnishlog -w /var/log/varnish/purge.log -q "ReqMethod eq PURGE or ReqMethod eq BAN"
8.3 监控脚本 #
bash
#!/bin/bash
# purge_monitor.sh
echo "=== Cache Invalidation Stats ==="
echo ""
echo "PURGE Count:"
varnishstat -1 -f MAIN.n_purges
echo ""
echo "BAN Count:"
varnishstat -1 -f MAIN.bans
echo ""
echo "BAN List:"
varnishadm ban.list | head -20
echo ""
echo "Recent PURGE Requests:"
varnishlog -d -q "ReqMethod eq PURGE" | head -20
九、失效最佳实践 #
9.1 失效策略 #
| 内容类型 | 失效策略 | TTL |
|---|---|---|
| 静态资源 | 版本化URL | 长TTL |
| 动态页面 | 主动失效 | 短TTL |
| API响应 | 事件驱动 | 极短TTL |
| 用户内容 | 按需失效 | 中等TTL |
9.2 失效时机 #
vcl
sub vcl_backend_response {
# 设置失效标记
if (bereq.url ~ "^/articles/") {
set beresp.http.Surrogate-Key = "articles";
set beresp.http.X-Cache-Tags = "articles,homepage";
}
}
9.3 避免过度失效 #
vcl
# 使用Grace避免频繁失效
sub vcl_backend_response {
set beresp.grace = 1h;
}
sub vcl_hit {
# 过期内容仍可服务
if (obj.ttl + obj.grace > 0s) {
return (deliver);
}
return (restart);
}
十、完整配置示例 #
vcl
vcl 4.1;
import std;
# ACL定义
acl purge {
"localhost";
"192.168.1.0"/24;
"10.0.0.0"/8;
}
# 后端定义
backend default {
.host = "127.0.0.1";
.port = "8080";
}
# 请求处理
sub vcl_recv {
# PURGE处理
if (req.method == "PURGE") {
if (!client.ip ~ purge) {
return (synth(405, "Not allowed."));
}
return (purge);
}
# BAN处理
if (req.method == "BAN") {
if (!client.ip ~ purge) {
return (synth(405, "Not allowed."));
}
# 基于标签失效
if (req.http.X-Ban-Tag) {
ban("obj.http.Surrogate-Key ~ " + req.http.X-Ban-Tag);
return (synth(200, "Banned by tag: " + req.http.X-Ban-Tag));
}
# 基于URL失效
if (req.http.X-Ban-Url) {
ban("req.url ~ " + req.http.X-Ban-Url);
return (synth(200, "Banned by URL: " + req.http.X-Ban-Url));
}
# 基于Host失效
if (req.http.X-Ban-Host) {
ban("req.http.Host == " + req.http.X-Ban-Host);
return (synth(200, "Banned by Host: " + req.http.X-Ban-Host));
}
# 基于内容类型失效
if (req.http.X-Ban-Type) {
ban("obj.http.Content-Type ~ " + req.http.X-Ban-Type);
return (synth(200, "Banned by Type: " + req.http.X-Ban-Type));
}
# 默认失效请求URL
ban("req.url ~ " + req.url);
return (synth(200, "Banned: " + req.url));
}
# 正常请求处理
if (req.method != "GET" && req.method != "HEAD") {
return (pass);
}
return (hash);
}
# 后端响应处理
sub vcl_backend_response {
# 设置Surrogate-Key
if (bereq.url ~ "^/articles/") {
set beresp.http.Surrogate-Key = "articles";
}
if (bereq.url ~ "^/products/") {
set beresp.http.Surrogate-Key = "products";
}
# 设置Grace
set beresp.grace = 1h;
}
# 合成响应
sub vcl_synth {
set resp.http.Content-Type = "application/json";
if (resp.status == 200) {
synthetic({"{"status":"ok","message":"} + resp.reason + {"}"});
} else {
synthetic({"{"status":"error","code":} + resp.status + {","message":"} + resp.reason + {"}"});
}
return (deliver);
}
十一、总结 #
本章我们学习了:
- 缓存失效概述:失效原因、方法对比
- PURGE方法:配置、使用、响应处理
- BAN方法:规则语法、高级配置、列表管理
- PURGE vs BAN:原理对比、选择建议
- Surrogate-Key:标签失效
- 主动失效:Webhook、CMS集成
- 批量失效:脚本、站点地图、正则
- 失效监控:统计、日志、监控脚本
掌握缓存失效后,让我们进入下一章,学习请求处理!
最后更新:2026-03-28