想象一个智能仓库管理员,常卖的货物他直接放在门口(缓存),客户来了不用进仓库就能取走。Varnish 就是这个"智能仓库管理员",让 Web 服务器减轻负载,用户访问更快!
📑 目录
- [什么是 Varnish?](#什么是 Varnish?)
- 名词解释(命令与概念)
- [Varnish 架构](#Varnish 架构)
- [Varnish 配置详解](#Varnish 配置详解)
- [Varnish 部署实战](#Varnish 部署实战)
- [Varnish 缓存策略](#Varnish 缓存策略)
- [Purge 与 Ban:缓存失效](#Purge 与 Ban:缓存失效)
- [Varnish 管理工具](#Varnish 管理工具)
- 性能优化
- 总结与官方参考
🎯 什么是 Varnish?
核心概念
Varnish 是一个高性能的 HTTP 加速器,也被称为反向代理缓存服务器,专注于缓存 Web 内容以减轻后端服务器负载。
✅ 缓存命中
❌ 缓存未命中
👤 客户端
📦 请求资源?
🚀 Varnish
直接返回
🔄 Varnish
请求后端
并缓存
🖥️ 后端服务器
Tomcat/Apache
⚡ 快速响应
减轻后端负载
为什么需要缓存? 后端每次动态生成页面会占用 CPU、数据库;把不变或少变的内容放在 Varnish,同一内容只算一次、卖多次,延迟低、后端压力小。
生活类比:
- Varnish = 商场的"快速提货区"
- 缓存命中 = 货物在快速提货区,直接拿走
- 缓存未命中 = 需要去后面的仓库取货
- 后端服务器 = 仓库,存储所有商品
Varnish vs 其他缓存方案

为什么选 Varnish 做专职缓存? 内存管理、哈希与锁针对缓存场景优化,VCL 可在不重启下加载新策略;Nginx 更偏「全能网关」,缓存只是其一;Squid 历史久、功能多但配置重。
| 特性 | Varnish | Squid | Nginx |
|---|---|---|---|
| 定位 | 专用缓存 | 代理+缓存 | 反向代理 |
| 性能 | 极高 | 中等 | 高 |
| 配置语言 | VCL | 配置文件 | 配置文件 |
| 缓存粒度 | 细粒度 | 粗粒度 | 粗粒度 |
| 生活类比 | 专职快递员 | 老式综合商店 | 现代购物中心 |
与其他软件对比(扩展)
下面从「反向代理 / 缓存 / 网关」角度,把 Varnish 与常见方案放在一起对比,便于按场景选型。
选型
🎯 要专职 HTTP 缓存?
✅ 选 Varnish
已有 Nginx/要一体化?
✅ Nginx proxy_cache
要边缘/防 DDoS?
✅ CDN
要正向代理/传统代理?
✅ Squid
要应用层 KV 缓存?
✅ Redis/Memcached
| 软件 | 定位 | 缓存能力 | 性能 | 配置/扩展 | 典型场景 | 与 Varnish 选型建议 |
|---|---|---|---|---|---|---|
| Varnish | 专用 HTTP 反向代理缓存 | 极强:VCL、purge/ban、grace、细粒度键 | 极高 | VCL、热加载 | 站前缓存、API 缓存、减轻后端 | 要专职缓存、高并发、可编程策略时首选。 |
| Nginx | 反向代理 + Web 服务器 + 缓存 | 中等:proxy_cache、proxy_cache_key 等 | 高 | 配置文件、Lua 等 | 负载均衡、动静分离、简单缓存 | 已有 Nginx、缓存需求不复杂时用 Nginx 即可;要更强缓存与策略用 Varnish。 |
| Squid | 正向/反向代理 + 缓存 | 强:ACL、refresh_pattern、purge | 中等 | 配置文件(较复杂) | 正向代理、企业上网缓存、传统反向缓存 | 要正向代理或已有 Squid 运维经验时可保留;新项目做反向缓存更推荐 Varnish。 |
| Apache Traffic Server | 反向代理 + 缓存(Apache 系) | 强:插件、规则、ESI | 高 | 配置文件、插件 | 大流量 CDN 节点、运营商级缓存 | 与 Varnish 同属「专职缓存」;ATS 偏 CDN 与插件生态,Varnish 偏 VCL 与自建控制。 |
| Caddy | 自动 HTTPS 的反向代理/Web 服务器 | 弱:无内置 HTTP 缓存 | 高 | Caddyfile、简单 | 自动证书、简单反向代理 | 不做 HTTP 页面缓存;要缓存需前置 Varnish 或 Nginx。 |
| HAProxy | 负载均衡 + 四层/七层代理 | 无:不缓存响应 | 极高(四层/七层转发) | 配置文件 | 负载均衡、高可用、SSL 终结 | 只做分流与高可用;缓存层用 Varnish/Nginx 放在 HAProxy 后面。 |
| CDN(Cloudflare/阿里云等) | 边缘加速 + 安全 | 强:边缘节点、就近命中、防 DDoS | 依赖节点与回源 | 控制台/API | 静态资源、全站加速、防攻击 | 要就近、免运维、按量付费 用 CDN;要自建、数据不出域、策略完全可控用 Varnish。 |
| Redis / Memcached | 应用层 KV 缓存 | 非 HTTP:键值存储,由应用读写 | 极高(内存) | 应用代码 | Session、接口结果、热点数据 | 是应用内缓存,不是 HTTP 反向缓存;可与 Varnish 并存:Varnish 缓存页面/接口响应,Redis 存 Session 等。 |
生活类比小结:
- Varnish:商场门口专职「快速提货区」管理员,只干缓存这一件事且干得极好。
- Nginx:商场大门+问询台+小仓库,啥都能干,仓库只是其中一块。
- Squid:老牌批发仓库,既能对外送货(反向)也能帮内部代购(正向),规矩多、配置厚。
- CDN:连锁店在各地开分店,顾客就近取货;总店(源站)前还可以再放一个 Varnish 做「总店门口提货区」。
📖 名词解释(命令与概念)
常用命令
| 命令/工具 | 说明 | 生活类比 | 为什么常用? |
|---|---|---|---|
| varnishd | Varnish 守护进程,真正干活的进程 | 仓库的「总控室」:启动后负责接单、查库存、叫仓库取货 | 为什么叫 daemon?后台常驻运行,不依赖终端;所有请求都由它处理。 |
| varnishadm | 管理接口,通过 CLI 与 varnishd 对话 | 经理用对讲机跟总控室说话:加载新规则、清缓存、看状态 | 为什么用 127.0.0.1:6082?管理口只监听本机,避免被外网误操作或攻击。 |
| varnishstat | 实时统计计数器(命中、未命中、连接数等) | 仓库门口的「今日提货/入库」大屏 | 为什么看 HIT/MISS?命中率高说明门口货多、回仓库少,后端压力小、响应快。 |
| varnishlog | 按请求/会话输出详细日志(可过滤) | 每笔交易的流水单:谁、要了什么、命中还是去仓库取的 | 为什么调试用?能精确看到某请求走了 lookup/pass/fetch 哪条路径。 |
| varnishncsa | 把日志转成 NCSA 兼容格式(类似 Apache access_log) | 把流水单整理成「标准报表」给分析工具用 | 为什么需要?很多监控、ELK 等只认 NCSA 格式,便于统一分析。 |
核心概念
| 概念 | 说明 | 生活类比 | 为什么重要? |
|---|---|---|---|
| VCL | Varnish Configuration Language,Varnish 专用配置语言 | 仓库的「作业手册」:什么货放门口、什么必须去仓库取、谁可以要求清空某格 | 为什么不用普通配置文件?缓存策略复杂(按 URL、Cookie、头、后端健康),需要条件分支和正则,VCL 专为此设计。 |
| backend | 后端服务器定义(host、port、探针等) | 仓库的「供应商/仓库房号」:去哪取货、多久算超时、怎么检查供应商是否在线 | 为什么有 probe?后端挂了若不知道,请求会一直打到死节点;探针定期探测,自动摘除故障后端。 |
| director | 多个 backend 的负载均衡器(轮询、随机等) | 多个仓库入口的「调度员」:轮流或随机选一个仓库取货 | 为什么用 director?单后端易成瓶颈;多后端分流并配合 probe 可做高可用。 |
| TTL | Time To Live,对象在缓存中的存活时间 | 门口货架的「保质期」:过了就认为过期,下次要就去仓库重新取 | 为什么不能无限大?内容会更新(如新闻、价格),TTL 太长会一直卖旧货。 |
| grace | 对象过期后仍可被使用的一段时间(常在后端故障时) | 仓库补货延迟时,「先卖库存里过期的」顶一阵,不直接报错 | 为什么有用?后端挂了或很慢时,返回稍旧的缓存总比 502 好,用户体验更稳。 |
| keep | 在 TTL 之外多保留一段时间,用于发 304 条件请求刷新 | 过期了但还留着「样品」,用来问后端:这批货有没有新版本?有就更新,没有就继续用样品 | 为什么能省带宽?304 只传头不传体,后端说「没变」就继续用缓存,减少回源流量。 |
| pass | 不查缓存,直接转发到后端且不缓存响应 | 客户要的是「定制/生鲜」,不放进门口货架,每次都去仓库现取 | 为什么 POST/登录等要 pass?会改状态或带 Cookie,缓存会错;必须实时走后端。 |
| pipe | 把客户端与后端之间打成「管道」,Varnish 只做 TCP 中转 | 客户和仓库直接「连麦」,管理员只负责接通,不拆包、不缓存 | 为什么 WebSocket 等要用 pipe?非 HTTP 或长连接,Varnish 不解析内容,直接透传更合适。 |
| hash | 查缓存:先算缓存键再 lookup,命中则交付、未命中则 fetch | 按「单号」(URL+Host 等)查门口有没有货,有就交付,没有就去仓库取并上架 | 为什么只缓存 GET/HEAD?规范上 GET 幂等、可缓存;POST 等会改数据,不能当同一份货反复卖。 |
| HIT | 缓存命中,直接从缓存返回 | 门口就有货,直接提走 | 为什么追求高 HIT?命中时无需问后端,延迟低、后端负载小。 |
| MISS | 缓存未命中,需从后端取并可选缓存 | 门口没有,去仓库取,取完可以放一份到门口 | 为什么有 hit-for-pass?某 URL 被标记为「永远不缓存」时,会记一条「不缓存」结果,避免反复去后端问。 |
| purge | 用 HTTP PURGE 等方法删除缓存中单个对象(精确 URL) | 经理说:「把 3 号货架第 2 格清空」------只清这一格 | 为什么只支持单 URL?语义简单、实现简单;精确失效时用。 |
| ban | 用表达式(如正则)让一批对象在下次被命中时视为失效 | 经理说:「所有 3 号货架上的东西都别卖了」------按规则批量失效 | 为什么 ban 是「下次命中时」?不立刻扫全缓存,避免卡顿;命中时再检查 ban 列表,匹配就不交付、去后端取。 |
| stevedore | Varnish 的存储引擎(如 malloc、file) | 门口货架用「内存」(malloc) 还是「磁盘文件」(file):内存快但重启丢,文件可持久、容量大 | 为什么有 file?内存贵且有限;大容量缓存用 file 存到 SSD/磁盘,重启后还能保留部分缓存。 |
相近概念对比
| 对比项 | 含义 | 何时用 | 生活类比 |
|---|---|---|---|
| pass vs pipe | pass:走 HTTP,Varnish 解析并转发,但不缓存;pipe:纯 TCP 隧道,不解析 | 普通 HTTP 但不缓存用 pass;WebSocket、CONNECT 等用 pipe | pass = 管理员代你去仓库取货并交给你;pipe = 管理员只给你接一条直通仓库的电话线 |
| hash vs pass | hash:查缓存,命中交付/未命中取后端;pass:不查缓存,直接后端 | 可缓存请求用 hash;登录、POST、管理后台等用 pass | hash = 先看门口有没有;pass = 一律去仓库,不摆门口 |
| purge vs ban | purge:删一个 URL 的缓存;ban:按表达式让一批对象失效(下次命中时生效) | 精确删一条用 purge;删整站/某目录/某后缀用 ban | purge = 清空 1 个格子;ban = 贴告示「这类货一律不准从门口卖」 |
| grace vs keep | grace:过期后还能当「保底」用;keep:过期后多留一段时间,用于发 304 刷新 | 防后端故障用 grace;想省带宽、支持 304 用 keep | grace = 断货时先卖临期品;keep = 留样品方便问供应商「有没有新款」 |
🏗️ Varnish 架构
工作原理
为什么请求要先进 vcl_recv? 这里是「入口」,决定这条请求是查缓存(hash)、绕开缓存(pass) 还是打管道(pipe),后续所有步骤都由此分支决定。
🔄 Varnish 工作流程
🔑 lookup
➡️ pass
🔌 pipe
是
否
👤 客户端请求
📥 VCL 接收
vcl_recv
📌 缓存策略?
📂 查询缓存
🖥️ 直接转发后端
📡 管道直连
✅ 命中?
📤 返回缓存
🔄 fetch 后端
💾 缓存并返回
📤 不缓存返回
📤 直连返回
VCL (Varnish Configuration Language)
VCL 是 Varnish 的专用配置语言,用于定义缓存策略。VCL 会被编译成 C 并加载到 worker 中执行,所以修改后需要「加载 + 使用」新 VCL 才能生效。
为什么 VCL 没有循环? 设计成「请求进来 → 一串判断 → return 一个动作」,避免复杂逻辑拖慢每请求的处理速度;复杂逻辑可放在 VMOD(C 扩展)里。
缓存
不缓存
是
否
📋 VCL 状态机
📥 vcl_recv
请求接收
📌 请求类型?
🔢 vcl_hash
计算哈希
➡️ vcl_pass
直接转发
🔍 查找缓存
✅ 命中?
✅ vcl_hit
返回缓存
❌ vcl_miss
去后端
📥 vcl_backend_response
从后端取回后处理
📤 vcl_deliver
交付
VCL 内置子例程(subroutines)
| 子例程 | 说明 | 使用场景 | 为什么在这个阶段? |
|---|---|---|---|
| vcl_recv | 请求接收后第一个入口 | 决定 return(hash)/pass/pipe/purge/synth | 必须先决定「查不查缓存、走不走管道」,后面才能分支。 |
| vcl_hash | 计算缓存键(可追加 hash_data) | 自定义缓存键(如按设备类型区分) | 键决定「同一请求是否命中同一对象」;默认用 req.url + Host 等。 |
| vcl_hit | 缓存命中时 | 可改为 pass 或 miss 重取 | 命中后通常直接 deliver,少数场景要强制刷新才改。 |
| vcl_miss | 缓存未命中时 | 通常 return(fetch),去后端取 | 未命中就必须去后端,fetch 后会进入 vcl_backend_response。 |
| vcl_backend_response | 从后端拿到响应后 | 设 TTL、grace、keep、是否缓存 | 后端响应在这里才能看到状态码、头、体,决定存不存、存多久。 |
| vcl_backend_error | 后端错误(超时、5xx)时 | 合成错误页或重试 | 为什么单独?后端挂了时可能想返回友好页或从别的后端重试。 |
| vcl_deliver | 即将把响应交给客户端前 | 加调试头(如 X-Cache: HIT/MISS) | 最后一环,可改响应头不改体,便于排查。 |
| vcl_synth | 使用 synthetic() 生成响应时 | 自定义错误页、重定向 | 不走后端,由 Varnish 直接返回内容。 |
⚙️ Varnish 配置详解
基础配置示例
vcl
# /etc/varnish/default.vcl
vcl 4.1;
import std;
# 后端服务器定义(为什么要有 .probe?后端挂了要自动摘除,避免请求一直打向死节点)
backend web1 {
.host = "192.168.1.20";
.port = "80";
.probe = {
.url = "/";
.timeout = 2s;
.interval = 5s;
.window = 5;
.threshold = 3;
}
}
backend web2 {
.host = "192.168.1.21";
.port = "80";
.probe = {
.url = "/";
.timeout = 2s;
.interval = 5s;
.window = 5;
.threshold = 3;
}
}
# 定义 directors
sub vcl_init {
# 轮询负载均衡
new web_servers = directors.round_robin();
web_servers.add_backend(web1);
web_servers.add_backend(web2);
}
# 请求接收
sub vcl_recv {
# 设置后端
set req.backend_hint = web_servers;
# 只缓存 GET 和 HEAD 请求
if (req.method != "GET" && req.method != "HEAD") {
return (pass);
}
# 不缓存特定路径
if (req.url ~ "^/admin") {
return (pass);
}
# 移除端口号
set req.http.Host = regsub(req.http.Host, ":[0-9]+", "");
# 去除 www
if (req.http.Host ~ "^(www\.).*") {
set req.http.Host = regsub(req.http.Host, "^www\.", "");
}
return (hash);
}
# 缓存命中
sub vcl_hit {
return (deliver);
}
# 缓存未命中
sub vcl_miss {
return (fetch);
}
# 从后端取回响应后的处理(Varnish 4.x 为 vcl_backend_response,不再使用 vcl_fetch)
sub vcl_backend_response {
# 设置缓存时长
if (beresp.http.Cache-Control ~ "max-age") {
unset beresp.http.Set-Cookie;
set beresp.ttl = std.duration(beresp.http.Cache-Control, "max-age");
}
# 不缓存有 Set-Cookie 的响应(特殊情况除外)
if (beresp.http.Set-Cookie) {
if (req.url ~ "^/api") {
unset beresp.http.Set-Cookie;
} else {
return (deliver);
}
}
return (deliver);
}
# 交付内容
sub vcl_deliver {
# 添加调试头
if (obj.hits > 0) {
set resp.http.X-Cache = "HIT";
} else {
set resp.http.X-Cache = "MISS";
}
return (deliver);
}
VCL 语法规则
为什么 VCL 用 ~ 做匹配? 沿用 Perl 习惯,~ 表示正则匹配;!~ 表示不匹配。字符串用双引号,长字符串可用 {"..."} 含换行。
| 规则 | 说明 | 示例 | 为什么? |
|---|---|---|---|
| 赋值 | set |
set req.http.Host = "example.com" |
请求/响应头、超时等都可改,决定后续行为。 |
| 取消 | unset |
unset beresp.http.Set-Cookie |
去掉 Set-Cookie 后响应才敢缓存,否则每用户不同。 |
| 正则匹配 | ~ |
if (req.url ~ "\.jpg$") |
URL、Host、User-Agent 等常用正则区分策略。 |
| 取反匹配 | !~ |
if (req.url !~ "^/static") |
排除某类请求(如非静态一律 pass)。 |
| 字符串替换 | regsub / regsuball |
regsub(req.url, "\.jpg$", ".png") |
归一化 URL(如去端口、去 www)便于缓存键一致。 |
| 终止 | return(action) |
return (hash) |
每个子例程必须 return 一个动作,否则用默认行为。 |
🚀 Varnish 部署实战
安装 Varnish
为什么用包管理器安装? 便于升级、依赖统一、与 systemd 集成,生产环境一般不推荐手编。
bash
# 1. 安装 Varnish(以 RHEL/CentOS 为例;Debian/Ubuntu 可用 apt install varnish)
yum install varnish -y
# 2. 开机自启并启动
systemctl enable varnish
systemctl start varnish
# 3. 检查状态(确认 Active: active (running))
systemctl status varnish
配置 Varnish
为什么监听 80 而管理口用 6082? 80 是用户流量入口;管理口单独端口且通常只绑 127.0.0.1,避免外网连上 varnishadm。为什么存储用 file? 内存(malloc) 快但重启即丢、容量受限于 RAM;file 可持久、容量大(如 10G),适合大缓存。
bash
# /etc/varnish/varnish.params
# Varnish 监听地址(0.0.0.0:80 表示所有网卡 80 端口)
VARNISH_LISTEN_ADDRESS=0.0.0.0:80
# 管理接口(仅本机,用于 varnishadm)
VARNISH_ADMIN_LISTEN_ADDRESS=127.0.0.1:6082
# 存储:malloc 为纯内存;file 为文件(可持久、大容量)
VARNISH_STORAGE="file,/var/lib/varnish/varnish_storage.bin,1G"
# worker 线程数(根据 CPU 与并发调整)
VARNISH_WORKER_THREADS=500
# 日志路径
VARNISH_LOGFILE="/var/log/varnish/varnish.log"
启动并测试
bash
# 1. 重启 Varnish(改 VCL 或 params 后通常需 restart)
systemctl restart varnish
# 2. 查看 Varnish 日志(进程/启动类)
tail -f /var/log/varnish/varnish.log
# 3. 管理 CLI:ping 确认与管理进程连通
varnishadm ping
# 4. 实时统计(HIT/MISS、连接数等)
varnishstat
# 5. 请求级日志(调试用,可按 X-Varnish 等过滤)
varnishlog
为什么改 VCL 后可以不重启进程? 用 varnishadm vcl.load + varnishadm vcl.use 加载并切换新 VCL,旧连接按旧逻辑跑完,新请求用新逻辑,实现「热加载」。
📊 Varnish 缓存策略
缓存决策树
GET/HEAD
POST/PUT/...
是
否
是
否
是
否
👤 客户端请求
📌 请求方法?
📦 可缓存?
➡️ 直接转发
pass
🔢 计算哈希
hash
📂 缓存中存在?
✅ 缓存命中
HIT
❌ 缓存未命中
MISS
🖥️ 从后端获取
📦 响应可缓存?
💾 缓存并返回
📤 不缓存直接返回
缓存最佳实践
| 策略 | 说明 | VCL 示例 | 生活类比 |
|---|---|---|---|
| 只缓存 GET | POST 等不缓存 | if (req.method != "GET") return (pass); |
只把「看货」当可缓存,「下单」每次都去仓库。 |
| 忽略 Cookie | 静态资源忽略 cookie | unset req.http.Cookie; |
图片/CSS/JS 不按人区分,去掉 Cookie 后同一 URL 共用一个缓存。 |
| 不缓存管理 | 管理页面不缓存 | if (req.url ~ "^/admin") return (pass); |
经理办公室的货不能放门口卖,必须每次进办公室取。 |
| 强制刷新 | PURGE 方法刷新缓存 | if (req.method == "PURGE") { return (purge); } |
经理说「3 号格清空」立即执行。 |
为什么只缓存 GET/HEAD? HTTP 语义里 GET/HEAD 是幂等的、不修改资源,可安全缓存;POST/PUT/DELETE 会改状态,缓存会导致重复提交或数据错乱。
🗑️ Purge 与 Ban:缓存失效
内容更新后需要让缓存失效,Varnish 提供 purge (精确删一条)和 ban(按条件批量失效)两种方式,对应「名词解释」中的对比。
Purge vs Ban 对比
📋 Ban
表达式/正则
varnishadm / HTTP BAN
下次命中时失效
🔪 Purge
单 URL
HTTP PURGE
立即删除
| 维度 | Purge | Ban |
|---|---|---|
| 作用范围 | 单个 URL(及 Vary 变体) | 匹配表达式的所有对象 |
| 是否支持正则 | 否,精确 URL | 是,如 req.url ~ "\.png$" |
| 生效时机 | 立即从缓存移除 | 对象下次被命中时再判断,匹配则不交付、回源取 |
| 典型用法 | 发布新文章后删首页缓存 | 全站换主题后 ban 所有 .css、或 ban obj.http.url ~ "/news/" |
为什么 ban 是「下次命中时」? 若每次 ban 都扫描全缓存,大缓存会卡顿;改为命中时再检查 ban 列表,CPU 分散到各请求,延迟更可控。需要立刻腾空间时可配合 TTL 或限制缓存大小。
Purge 示例(VCL)
只允许本机和内网执行 PURGE,避免被滥用:
vcl
acl purge_allowed {
"localhost";
"127.0.0.1";
"192.168.0.0"/24;
}
sub vcl_recv {
if (req.method == "PURGE") {
if (!client.ip ~ purge_allowed) {
return (synth(405, "Method Not Allowed"));
}
return (purge);
}
}
为什么 PURGE 要加 ACL? PURGE 会删缓存,若对公网开放,攻击者可反复 purge 导致缓存雪崩、全部回源,所以只允许可信 IP。
Ban 示例(命令行 + 可选 HTTP BAN)
bash
# 清除所有 .png(按 URL 正则)
varnishadm ban "req.url ~ '\.png$'"
# 清除某主机的某路径
varnishadm ban "req.http.host == 'www.example.com' && req.url ~ '^/api/'"
# 查看当前 ban 列表
varnishadm ban.list
通过 HTTP 发 BAN(需在 VCL 里实现,例如用 std.ban()):
vcl
import std;
sub vcl_recv {
if (req.method == "BAN") {
if (!client.ip ~ purge_allowed) {
return (synth(403, "Forbidden"));
}
if (std.ban("req.http.host == " + req.http.host + " && req.url == " + req.url)) {
return (synth(200, "Ban added"));
}
return (synth(400, std.ban_error()));
}
}
为什么 ban 用 obj. 更友好? * ban lurker 后台线程只处理只涉及 obj.* 的 ban,能持续清理匹配对象;若 ban 里只用 req.*,lurker 无法用请求上下文,只能等请求命中时再判,ban 列表可能积压。
🔧 Varnish 管理工具
varnishadm 命令
为什么 vcl.load 要起个名字? 可同时加载多份 VCL(如 new_config、backup),用名字区分;切换时 vcl.use new_config 指定用哪一份,便于回滚。
bash
# 查看与管理进程是否连通
varnishadm ping
# 查看运行参数(线程数、storage 等)
varnishadm param.show
# 加载新 VCL(名字 + 来源:default 表示用 -f 指定的主文件)
varnishadm vcl.load new_config default.vcl
# 切换为刚加载的 VCL(新请求立即用新配置)
varnishadm vcl.use new_config
# 按条件 ban(正则),不是删单条 URL
varnishadm ban req.url "^/static/.*"
# 丢弃未使用的 VCL 副本(释放引用)
varnishadm vcl.discard old_config
varnishstat 监控
为什么关注 cache_hit 与 cache_miss? 命中多说明缓存有效、后端压力小;命中率 = cache_hit / (cache_hit + cache_miss),通常希望 > 90% 以上(视业务而定)。
bash
# 实时统计(每秒刷新)
varnishstat
# 单次输出后退出(便于脚本采集)
varnishstat -1
# 只看某计数器
varnishstat -1 -f cache_hit -f cache_miss
| 常用计数器 | 含义 | 生活类比 |
|---|---|---|
| MAIN.cache_hit | 缓存命中次数 | 门口直接提货次数 |
| MAIN.cache_miss | 缓存未命中次数 | 去仓库取货次数 |
| MAIN.sess_conn | 接受的客户端连接数 | 接待的客户数 |
| MGT.uptime | 进程运行时长 | 仓库开门了多久 |
💡 性能优化
为什么线程池要设 min/max? min 保证始终有足够 worker 应对突发;max 防止连接爆炸时无限开线程拖垮机器。workspace 调大可处理更大请求/响应头,但占内存。
调优参数
bash
# /etc/systemd/system/varnish.service.d/custom.conf
[Service]
# 增加文件描述符限制
LimitNOFILE=131072
# 增加线程数
ExecStart=
ExecStart=/usr/sbin/varnishd \
-a :80 \
-T localhost:6082 \
-f /etc/varnish/default.vcl \
-s file,/var/lib/varnish/varnish_storage.bin,10G \
-p thread_pool_min=200 \
-p thread_pool_max=4000 \
-p workspace_client=128k \
-p workspace_backend=128k
缓存命中率优化
为什么命中率重要? 命中时无需问后端,延迟低、带宽省、后端 CPU 省;命中率每提高一截,后端负载就明显下降。
📈 提高缓存命中率
⏱️ 增加 TTL
📁 缓存静态资源
🔧 忽略动态参数
🛡️ 使用 grace 模式
⬇️ 减少回源
🖥️ 后端故障时用旧缓存保底
VCL 缓存优化配置:
vcl
sub vcl_backend_response {
# 对于图片、CSS、JS,缓存 1 天
if (bereq.url ~ "\.(jpg|jpeg|png|gif|css|js)$") {
set beresp.ttl = 86400s;
unset beresp.http.Set-Cookie;
}
# 对于 HTML,缓存 1 小时
if (bereq.url ~ "\.html$") {
set beresp.ttl = 3600s;
}
# grace 模式:后端故障时使用旧缓存
set beresp.grace = 3600s;
return (deliver);
}
🎯 总结与官方参考
Varnish 是专业的 HTTP 缓存服务器,能够大幅提升 Web 性能和减轻后端负载。
核心要点记忆口诀:
- Varnish 专缓存,性能极高是王道
- VCL 语言强,灵活配置没商量
- 动静要分离,静态资源缓存好
- grace 模式妙,后端故障能保底
- 监控要做好,命中率要盯牢
生活类比总结:
- Varnish = 智能仓库管理员
- 缓存命中 = 快速提货区直接拿走
- 缓存未命中 = 去仓库取货
- grace 模式 = 仓库补货时先卖库存
- ban 清理 = 主动清理过期商品
官方文档与延伸阅读
| 资源 | 说明 |
|---|---|
| Varnish Cache 官方文档 | 安装、配置、VCL 参考 |
| VCL 4.1 参考 | 语法、变量、子例程、backend、probe |
| Purging and banning | Purge 与 Ban 的官方说明 |
| varnishd 参数 | 启动参数、storage、线程等 |
相近方案简要对比
更完整的对比见前文 与其他软件对比(扩展)。
| 方案 | 适用场景 | 与 Varnish 对比 |
|---|---|---|
| Nginx proxy_cache | 已有 Nginx、缓存需求不复杂 | 配置简单、无需单独进程;缓存能力与灵活性不如 Varnish。 |
| Squid | 传统正向/反向代理 + 缓存 | 功能多、历史久;性能与配置灵活性一般不如 Varnish。 |
| Apache Traffic Server | 大流量、CDN 节点、插件化 | 同属高性能缓存;ATS 偏 CDN/插件,Varnish 偏 VCL 自建。 |
| Caddy | 自动 HTTPS、简单反向代理 | 无内置 HTTP 缓存;做缓存需前挂 Varnish 或 Nginx。 |
| HAProxy | 负载均衡、高可用、SSL 终结 | 不缓存;常与 Varnish 搭配:HAProxy 分流,Varnish 做缓存层。 |
| CDN(Cloudflare、阿里云等) | 静态资源、全站加速、防 DDoS | 边缘节点、就近命中;Varnish 自建、可控、无按量费用。 |
| Redis / Memcached | Session、接口结果、热点 KV | 应用层缓存,非 HTTP 反向缓存;可与 Varnish 并存(Varnish 缓存页面,Redis 存 Session 等)。 |
最后提醒:Varnish 缓存虽好,但动态内容要谨慎!Session、管理页面、带 Cookie 的个性化内容等绝对不能缓存;否则会出现用户 A 看到用户 B 的数据等严重问题。