Webman单靠Cache::set()无法实现真正页面级缓存,因仍需执行路由、中间件、模板编译及DB查询;必须由Varnish在HTTP层拦截并返回完整HTML,配合VCL清除敏感头、设置TTL、支持BAN失效等机制才能落地高性能缓存。

Webman 本身不处理 HTTP 缓存层,Varnish 是它真正落地页面级缓存的必要搭档;单靠 Webman 的 Cache::set() 或响应头控制,无法规避 PHP 层重复渲染、模板编译、数据库查询等开销——必须让 Varnish 在 Webman 前端拦截并直接返回完整 HTML。
为什么不能只用 Webman 的 Cache::set() 做页面缓存
很多人试图在控制器里 Cache::set('page_home', $html, 300),再 Cache::get('page_home') 返回,这看似“缓存了页面”,实则问题一堆:
- 每次请求仍会进入 Webman 路由、中间件、控制器,PHP 解析和协程调度开销照旧
- 模板仍要经历词法解析 → AST → 编译 PHP 文件流程(哪怕已预热,首次并发仍可能锁竞争)
-
Cache::set()存的是字符串,不是 HTTP 响应实体,无法携带ETag、Vary、Cache-Control等关键缓存语义 - 移动端/桌面端、登录态/游客态等多维度内容无法靠一个 key 区分,容易缓存污染
Varnish 必须识别 Webman 的响应特征才能正确缓存
Varnish 默认不缓存带 Set-Cookie、Authorization 或状态码非 200/301/302 的响应。而 Webman 默认会在登录后写 Set-Cookie: webman_session=...,导致首页、列表页全被绕过缓存。
解决方法是在 vcl_backend_response 和 vcl_deliver 中主动干预:
立即学习“PHP免费学习笔记(深入)”;
- 对不需要用户态的公开页面(如 /、/about、/api/docs),在 Webman 中显式清除 cookie:
response()->withCookie('', '', -1),或更稳妥地在中间件中调用$response->withoutCookie('webman_session') - 在 VCL 中添加规则,允许缓存无敏感头的 200 响应:
sub vcl_backend_response { if (beresp.status == 200 && bereq.url ~ "^/(|about|blog)" && !bereq.http.Authorization) { unset beresp.http.Set-Cookie; set beresp.ttl = 5m; set beresp.grace = 2m; } } - 若需区分 PC/移动端,Webman 应在响应头中输出
X-Device-Type: mobile,VCL 中用hash_data(req.http.X-Device-Type)加入 hash 键,避免混存
Webman 需配合 VCL 输出可缓存的语义化响应头
Varnish 不看内容,只认头。Webman 控制器必须主动声明哪些页面可缓存、如何区分、何时失效:
- 静态内容页(如帮助中心):返回
Cache-Control: public, max-age=3600, stale-while-revalidate=86400 - 带分页参数的列表页:用
Vary: X-Page-Num, X-Page-Size(需前端 JS 或 Nginx 注入该头,Webman 本身不接收这些 header,但可信任并透传) - 用户无关的 API 文档页:加
ETag: "v2-".md5($content),VCL 中启用if (req.http.If-None-Match == resp.http.ETag) { return (synth(304)); } - 禁止缓存含用户信息的页面:明确设
Cache-Control: private, no-store,VCL 中匹配该头后直接return (pass)
缓存失效不能只靠 TTL,得有主动驱逐能力
Webman 更新文章、修改商品价格后,Varnish 缓存不会自动更新。靠 5 分钟 TTL 等刷新太慢,且无法精准命中。
推荐组合方案:
- Webman 后台操作完成时,用
curl -X BAN "http://varnish-host/article/123"触发 VCL 的vcl_recv中的if (req.method == "BAN") { ban("req.url ~ " + req.url); return (synth(200,"Banned")); } - 对全站通用变更(如语言包升级、主题切换),用
ban("req.http.host == example.com")清全局 - 避免用
purge——它只清当前节点;Webman 集群部署时,必须用ban广播到所有 Varnish 实例(通过共享 Redis 或 Consul 同步) - Webman 中不要用
file_put_contents()写 runtime 文件来触发 reload,Varnish 不感知文件系统变化
最易被忽略的一点:Varnish 的 ban 列表是内存结构,重启即丢。生产环境必须配置 ban-lurker 后台线程定期清理过期规则,否则 ban 列表持续膨胀拖慢 lookup 性能。



















