toflar/psr6-symfony-http-cache-store

为Symfony的HttpCache反向代理提供的替代存储实现,支持自动删除过期条目和基于标签的缓存失效。

4.2.0 2023-09-18 14:46 UTC

README

支持的分支

  • 对于PHP ^7.2和Symfony <6,使用版本3.x
  • 对于PHP ^8.0和Symfony >6,使用版本4.x

简介

Symfony的HttpCache存储实现相对较旧,是在还没有单独的锁定和缓存组件的时候开发的。此外,过期的缓存条目从未被删除,因此您的缓存目录将永远继续增长,直到您手动删除它。

在过程中,我需要支持基于标签的缓存失效,这要归功于Symfony Cache组件,实施起来相当简单。

因此,这个包提供了一个替代的StoreInterface实现,...

  • ...而不是再次实现锁定和缓存机制,而是使用经过良好测试的Symfony Cache和Lock组件,默认情况下都使用本地文件系统适配器。
  • ...得益于Cache组件的TagAwareAdapterInterface,支持基于标签的缓存失效。
  • ...得益于Cache组件的PrunableInterface,支持在文件系统上自动删除过期的条目,以尝试防止文件系统被淹没。
  • ...允许您使用不同的PSR-6缓存适配器以及不同的锁适配器,而不是本地文件系统适配器。但是,请小心选择合适的适配器,请见下方的警告。
  • ...支持BinaryFileResponse实例。

安装

$ composer require toflar/psr6-symfony-http-cache-store

配置

对于Symfony 4/Flex结构,您需要调整您的index.php如下

<?php

// public/index.php
$kernel = new Kernel($env, $debug);
$kernel = new HttpCache(
    $kernel,
    new Psr6Store(['cache_directory' => $kernel->getCacheDir()]),
    null,
    ['debug' => $debug]
);

就是这样,这就是所有要做的事情。Psr6Store将自动创建最适合您本地文件系统的最佳缓存和锁定适配器。

如果您想更进一步,可以通过构造函数传入一个包含$options的数组来配置Psr6Store

  • cache_directory:默认缓存适配器和锁工厂的缓存目录的路径。

    需要此选项或同时需要cachelock_factory

    类型字符串

  • cache:明确指定您想使用的缓存适配器。

    注意,如果您想使用缓存标签,此缓存必须实现Symfony\Component\Cache\Adapter\TagAwareAdapterInterface。确保lockcache具有相同的范围。请参阅下方的警告!

    类型Symfony\Component\Cache\Adapter\AdapterInterface 默认FilesystemAdapter实例,带有cache_directory

  • lock_factory:明确指定您想使用的锁工厂。确保锁和缓存具有相同的范围。请参阅下方的警告!

    类型Symfony\Component\Lock\Factory 默认:如果支持,则为带有SemaphoreStoreFactory,否则为FlockStore

  • prune_threshold:配置写入操作的数量,直到存储将删除过期的缓存条目。传递0以禁用自动删除。

    类型整数 默认500

  • cache_tags_header:用于检查标签的HTTP头名称。

    类型字符串 默认Cache-Tags

  • generate_content_digests:是否生成内容摘要。有关更多信息,请参阅“生成内容摘要”。

    类型布尔值 默认true

生成内容摘要

默认情况下,此缓存实现会生成内容摘要。这意味着响应元数据与响应内容存储在单独的地方。如果多个响应具有相同的内容,它只会在缓存中存储一次。比较以下说明以了解差异

生成内容摘要:

Illustration of the cache with generating content digests

不生成内容摘要:

Illustration of the cache without generating content digests

生成内容摘要优化了缓存,使其使用的存储空间更少。然而,在查找过程中,使用它们也带来了需要第二次往返缓存以获取内容摘要的代价。

是否要使用内容摘要取决于您的PSR-6缓存后端。如果查找速度快且存储空间有限(例如Redis),您可能想使用内容摘要。如果查找较慢且存储不是问题(例如文件系统),您可能想禁用它们。

您可以使用generate_content_digests配置选项来控制这种行为。

缓存BinaryFileResponse实例

此缓存实现允许缓存BinaryFileResponse实例,但文件实际上并不会复制到缓存目录。它只会尝试获取原始文件,如果该文件不存在,则存储返回null,导致HttpCache将其视为缓存未命中并正常继续。这对于需要防止应用程序启动并从HttpCache提供响应的场景(例如缓存/favicon.ico请求)非常理想。

缓存标记

通过添加包含标签的响应头(以逗号分隔的值)来标记缓存条目。默认情况下,该头名为Cache-Tags,可以在cache_tags_header中重写。

要使标签失效,请调用方法Psr6Store::invalidateTags或使用来自FOSHttpCache库的PurgeTagsListener来处理标签失效请求。

修剪过期的缓存项

默认情况下,此存储在每次执行500次缓存写入操作后从缓存中删除过期的条目。获取数据不会影响性能。您可以通过prune_threshold配置设置更改自动修剪的频率。

您还可以通过在缓存上调用prune()方法手动触发修剪。例如,您可以实现一个cron作业,在配置的间隔加载存储并修剪它,以防止由于修剪而减慢随机请求的速度,这些请求是缓存未命中,因为它们必须等待修剪。如果您已设置cron作业,您应通过将阈值设置为0来禁用自动修剪。

警告

您可以配置除文件系统以外的其他缓存适配器或锁存储。只有当您确定自己在做什么时才这样做。在此请求中,Fabien拒绝了将PSR-6存储支持添加到Symfony的AppCache,以下是其论据:

  • 使用文件系统允许opcache使缓存非常有效;
  • 缓存包含一些PHP(例如,当使用ESI时)且存储PHP不在文件系统中意味着eval()来自Redis / Memcache /...的字符串;
  • HttpCache非常早地触发,并且无法访问容器或任何其他内容。它应该保持这种方式以保持高效。

虽然第一点和第三点取决于您所做和所需的内容,但请确保遵守第二点。如果您使用Redis或Memcache等网络启用缓存,请确保它们是共享的。如果使用Redis或Memcache,请确保它们是共享的。

致谢

我想感谢David在为我们为酷炫的FOSHttpCache库集成工作时提供的宝贵反馈。