HTTP 缓存
富 Web 应用程序的本质意味着它们是动态的。无论你的应用程序多么高效,每个请求总是包含比服务静态文件更多的开销。通常,这很好。但是,当您需要您的请求像闪电般快速时,您需要 HTTP 缓存。
站在巨人的肩膀上进行缓存
通过 HTTP 缓存,您可以缓存页面的完整输出(即响应),并在后续请求中完全绕过您的应用程序。对于高度动态的网站,缓存整个响应并非总是可行,真是这样吗?使用 边缘端包含(ESI),您可以在您网站的仅片段上使用 HTTP 缓存的强大功能。
Symfony 缓存系统与众不同,因为它依赖于 RFC 7234 - 缓存 中定义的 HTTP 缓存的简洁性和强大功能。Symfony 没有重新发明缓存方法,而是拥抱了定义 Web 上基本通信的标准。一旦你理解了基本的 HTTP 验证和过期缓存模型,你就可以掌握 Symfony 缓存系统了。
由于使用 HTTP 缓存并非 Symfony 独有,因此已经存在许多关于该主题的文章。如果您是 HTTP 缓存的新手,强烈推荐 Ryan Tomayko 的文章 Things Caches Do。另一个深入的资源是 Mark Nottingham 的 Cache Tutorial。
使用网关缓存进行缓存
当使用 HTTP 缓存时,缓存与您的应用程序完全分离,并位于您的应用程序和发出请求的客户端之间。
缓存的工作是接受来自客户端的请求并将它们传递回您的应用程序。缓存还将接收来自您的应用程序的响应,并将它们转发给客户端。缓存是客户端和您的应用程序之间请求-响应通信的“中间人”。
在此过程中,缓存将存储每个被视为“可缓存”的响应(请参阅 HTTP 缓存)。如果再次请求相同的资源,缓存会将缓存的响应发送给客户端,完全忽略您的应用程序。
这种类型的缓存被称为 HTTP 网关缓存,并且存在许多,例如 Varnish、反向代理模式下的 Squid 和 Symfony 反向代理。
提示
网关缓存有时被称为反向代理缓存、替代缓存,甚至 HTTP 加速器。
Symfony 反向代理
Symfony 配备了一个用 PHP 编写的反向代理(即网关缓存)。它不像 Varnish 那样是一个功能齐全的反向代理缓存,但它是一个很好的入门方式。
提示
有关设置 Varnish 的详细信息,请参阅 如何使用 Varnish 加速我的网站。
使用 framework.http_cache
选项为 prod 环境 启用代理
1 2 3 4
# config/packages/framework.yaml
when@prod:
framework:
http_cache: true
内核将立即充当反向代理:缓存来自您的应用程序的响应并将它们返回给客户端。
代理具有合理的默认配置,但可以通过 一组选项 进行微调。
当处于 调试模式 时,Symfony 会自动向响应添加 X-Symfony-Cache
标头。您还可以使用 trace_level
配置选项并将其设置为 none
、short
或 full
以添加此信息。
short
将仅添加主要请求的信息。它以简洁的方式编写,可以轻松地将信息记录在服务器日志文件中。例如,在 Apache 中,您可以在 LogFormat
格式语句中使用 %{X-Symfony-Cache}o
。此信息可用于提取有关您的路由的缓存效率的常规信息。
提示
您可以使用 trace_header
配置选项更改用于跟踪信息的标头的名称。
使你的响应支持 HTTP 缓存
一旦您添加了反向代理缓存(例如像 Symfony 反向代理或 Varnish),您就可以缓存您的响应了。为此,您需要告知您的缓存哪些响应是可缓存的以及缓存多长时间。这是通过在响应上设置 HTTP 缓存标头来完成的。
HTTP 指定了四个响应缓存标头,您可以设置它们以启用缓存
Cache-Control
Expires
ETag
Last-Modified
这四个标头用于通过两种不同的模型帮助缓存您的响应
过期缓存
缓存响应的最简单方法是将其缓存特定时间量
1 2 3 4 5 6 7 8 9
// src/Controller/BlogController.php
use Symfony\Component\HttpKernel\Attribute\Cache;
// ...
#[Cache(public: true, maxage: 3600, mustRevalidate: true)]
public function index(): Response
{
return $this->render('blog/index.html.twig', []);
}
由于这段新代码,您的 HTTP 响应将具有以下标头
1
Cache-Control: public, maxage=3600, must-revalidate
这告诉您的 HTTP 反向代理将此响应缓存 3600 秒。如果任何人在 3600 秒之前再次请求此 URL,则根本不会访问您的应用程序。如果您正在使用 Symfony 反向代理,请查看 X-Symfony-Cache
标头以获取有关缓存命中和未命中的调试信息。
提示
请求的 URI 用作缓存键(除非您 改变)。
这提供了出色的性能,并且易于使用。但是,不支持缓存失效。如果您的内容发生更改,您需要等到缓存过期页面才会更新。
提示
实际上,您可以手动使您的缓存失效,但它不是 HTTP 缓存规范的一部分。请参阅 缓存失效。
如果您需要为许多不同的控制器操作设置缓存标头,请查看 FOSHttpCacheBundle。它提供了一种基于 URL 模式和其他请求属性定义缓存标头的方法。
最后,有关过期缓存的更多信息,请参阅 HTTP 缓存过期。
验证缓存
使用过期缓存,您说“缓存 3600 秒!”。但是,当有人更新缓存内容时,您将无法在您的网站上看到该内容,直到缓存过期。
如果您需要立即查看更新的内容,您需要 使 您的缓存失效或使用验证缓存模型。
有关详细信息,请参阅 HTTP 缓存验证。
安全方法:仅缓存 GET 或 HEAD 请求
HTTP 缓存仅适用于“安全”的 HTTP 方法(如 GET 和 HEAD)。这意味着三件事
- 不要尝试缓存 PUT 或 DELETE 请求。它不会工作,并且有充分的理由。这些方法旨在在改变您的应用程序状态时使用(例如,删除博客文章)。缓存它们会阻止某些请求命中和改变您的应用程序。
- POST 请求通常被认为不可缓存,但是当它们包含显式的新鲜度信息时,它们可以被缓存。但是,POST 缓存尚未得到广泛实施,因此如果可能,您应该避免使用它。
- 在响应 GET 或 HEAD 请求时,您绝不应该更改应用程序的状态(例如,更新博客文章)。如果这些请求被缓存,未来的请求可能实际上不会访问您的服务器。
更多响应方法
Response 类提供了更多与缓存相关的方法。以下是最有用的方法
1 2 3 4 5
// marks the Response stale
$response->expire();
// forces the response to return a proper 304 response with no content
$response->setNotModified();
此外,大多数与缓存相关的 HTTP 标头可以通过单个 setCache() 方法设置
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
// use this method to set several cache settings in one call
// (this example lists all the available cache settings)
$response->setCache([
'must_revalidate' => false,
'no_cache' => false,
'no_store' => false,
'no_transform' => false,
'public' => true,
'private' => false,
'proxy_revalidate' => false,
'max_age' => 600,
's_maxage' => 600,
'immutable' => true,
'last_modified' => new \DateTime(),
'etag' => 'abcdef'
]);
提示
使用 #[Cache]
属性时,所有这些选项也可用。
使用边缘端包含(ESI)
当页面包含动态部分时,您可能无法缓存整个页面,而只能缓存部分页面。阅读 使用边缘端包含(ESI) 以了解如何为页面的特定部分配置不同的缓存策略。
HTTP 缓存和用户会话
每当在请求期间启动会话时,Symfony 都会将响应转换为私有的不可缓存的响应。这是不缓存私人用户信息(例如购物车、用户个人资料详细信息等)并将其暴露给其他访问者的最佳默认行为。
但是,即使使用会话的请求在某些情况下也可以缓存。例如,与某些用户组相关的信息可以为属于该组的所有用户缓存。处理这些高级缓存场景超出了 Symfony 的范围,但可以使用 FOSHttpCacheBundle 解决它们。
为了禁用使使用会话的请求不可缓存的默认 Symfony 行为,请将以下内部标头添加到您的响应中,Symfony 将不会修改它
1 2 3
use Symfony\Component\HttpKernel\EventListener\AbstractSessionListener;
$response->headers->set(AbstractSessionListener::NO_AUTO_CACHE_CONTROL_HEADER, 'true');
总结
Symfony 的设计旨在遵循经过验证的规则:HTTP。缓存也不例外。掌握 Symfony 缓存系统意味着熟悉 HTTP 缓存模型并有效地使用它们。这意味着,您不仅可以依赖 Symfony 文档和代码示例,还可以访问与 HTTP 缓存和网关缓存(如 Varnish)相关的知识世界。