跳到内容

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 配置选项并将其设置为 noneshortfull 以添加此信息。

short 将仅添加主要请求的信息。它以简洁的方式编写,可以轻松地将信息记录在服务器日志文件中。例如,在 Apache 中,您可以在 LogFormat 格式语句中使用 %{X-Symfony-Cache}o。此信息可用于提取有关您的路由的缓存效率的常规信息。

提示

您可以使用 trace_header 配置选项更改用于跟踪信息的标头的名称。

Symfony 反向代理是在开发网站或将网站部署到您无法安装 PHP 代码之外的任何共享主机时使用的绝佳工具。但是由于是用 PHP 编写的,因此它不可能像用 C 编写的代理那样快。

幸运的是,由于所有反向代理实际上都是相同的,因此您应该能够毫无问题地切换到更强大的东西——例如 Varnish。请参阅 如何使用 Varnish

使你的响应支持 HTTP 缓存

一旦您添加了反向代理缓存(例如像 Symfony 反向代理或 Varnish),您就可以缓存您的响应了。为此,您需要告知您的缓存哪些响应是可缓存的以及缓存多长时间。这是通过在响应上设置 HTTP 缓存标头来完成的。

HTTP 指定了四个响应缓存标头,您可以设置它们以启用缓存

  • Cache-Control
  • Expires
  • ETag
  • Last-Modified

这四个标头用于通过两种不同的模型帮助缓存您的响应

  1. 过期缓存 用于将您的整个响应缓存特定时间量(例如 24 小时)。简单,但缓存失效更困难。
  2. 验证缓存 更复杂:用于缓存您的响应,但允许您在内容更改后立即动态使其失效。

您将读到的所有 HTTP 标头都不是由 Symfony 发明的!它们是整个 Web 站点使用的 HTTP 规范的一部分。要深入了解 HTTP 缓存,请查看文档 RFC 7234 - 缓存RFC 7232 - 条件请求

作为一名 Web 开发人员,强烈建议您阅读规范。它的清晰性和强大功能——即使在创建十五年后——也是非常宝贵的。不要被规范的外观吓倒——它的内容比它的封面更漂亮!

过期缓存

缓存响应的最简单方法是将其缓存特定时间量

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] 属性时,所有这些选项也可用。

缓存失效

缓存失效不是 HTTP 规范的一部分。尽管如此,在您网站上的某些内容更新后立即删除各种 HTTP 缓存条目可能非常有用。

有关详细信息,请参阅 缓存失效

使用边缘端包含(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)相关的知识世界。

本作品(包括代码示例)根据 Creative Commons BY-SA 3.0 许可获得许可。
目录
    版本