mmamedov / page-cache
PageCache 是一个轻量级的 PHP 完整页面缓存库。它使用各种策略来区分同一页面的不同版本。
Requires
- php: >=5.6.0
- psr/log: ^1.0
- psr/simple-cache: ^1.0
Requires (Dev)
- cache/integration-tests: dev-master
- guzzlehttp/guzzle: ^6.3
- mikey179/vfsstream: ^1.6
- mobiledetect/mobiledetectlib: ^2.8
- monolog/monolog: ^1.23
- phpunit/phpunit: ^5
- symfony/cache: ^3.4 || ^4.0
- symfony/process: ^3.4 || ^4.0
Suggests
- mobiledetect/mobiledetectlib: Allows separate cache versions for mobile pages
- monolog/monolog: Allows using monolog for logging
README
全页面 PHP 缓存库
PageCache 是一个轻量级的 PHP 完整页面缓存库,无需配置即可直接使用。当您需要一个简单而强大的基于文件的 PHP 缓存解决方案时使用它。移动设备页面缓存已内置。
使用 Composer 安装 PHP PageCache 并开始缓存您的 PHP 浏览器输出代码
composer require mmamedov/page-cache
或手动添加到您的 composer.json 文件中
{ "require": { "mmamedov/page-cache": "^2.0" } }
安装 PageCache 后,包含 Composer 的 autoload.php 文件,或实现您自己的自动加载器。推荐使用 Composer 自动加载器。
请勿使用 master
分支,因为它可能包含不稳定代码,请使用版本化分支。
升级到 v2.*
2.0 版本与以 v1.0 开头的版本不兼容。2.0 版本引入了新功能,代码重构以提供更多功能。
升级到版本 2.0 时请注意以下事项
- PHP 要求 >= 5.6。
- 您的配置文件必须如下所示
return [...]
而不是像前一个版本中的$config = array(...);
。 - 配置
expiration
设置已重命名为cache_expiration_in_seconds
- 使用
try/catch
确保在 PageCache 错误的情况下正确加载页面。
如果您发现任何其他显著的兼容性问题,请告知我们,我们将在此处包括它们。
无数据库调用
页面一旦被缓存,就不再需要数据库调用!即使您的页面包含许多数据库调用和复杂逻辑,它也只会执行一次并缓存您指定的周期。不再超载!
这是一个非常高效和简单的方法,用于缓存您最常访问的动态页面。 Tmawto.com 网站基于 PageCache 构建,非常快。
为什么还需要另一个 PHP 缓存类?
简短回答 - 简单性。如果您想在您的动态 PHP 页面顶部包含几行代码并能完全缓存它们,那么 PageCache 适合您。无需为每个 URL 设置缓存文件名,无需担心您动态生成的 URL 参数和更改的 URL。PageCache 会检测这些更改并相应地缓存。
PageCache 还会检测 $_SESSION 的更改并正确缓存这些页面。如果您在网站上启用了用户认证,并且页面内容根据用户登录而更改,而 URL 保持不变,这将非常有用。
许多缓存解决方案侧重于基于关键词的方法,您需要为您的内容设置一个关键词(无论是完整页面缓存、变量等)。对于基于关键词的方法有许多优秀的包。也可以使用更复杂的解决方案,如缓存代理 Varnish。另一方面,PageCache 是一个简单的仅全页面缓存解决方案,它确实如其名称所述 - 使用 PHP 生成页面缓存。
PageCache 的工作原理
PageCache 不会要求您提供关键词,它会根据实现 StrategyInterface 的策略自动生成它们。您可以根据应用程序的需求定义自己的命名策略。策略类负责为当前请求生成唯一的键,键成为缓存文件的文件名(如果使用文件系统存储)。
<?php require_once __DIR__.'/../vendor/autoload.php'; try { $cache = new PageCache\PageCache(); $cache->config() ->setCachePath('/your/path/') ->setEnableLog(true); $cache->init(); } catch (\Exception $e) { // Log PageCache error or simply do nothing. // In case of PageCache error, page will load normally, without cache. } //rest of your PHP page code, everything below will be cached
使用 PSR-16 兼容的缓存适配器
PageCache基于PSR-16 SimpleCache "键"=>"值"存储,并在类FileSystemPsrCacheAdapter
中具有默认的基于文件的缓存适配器。这种实现方式快速且在内部使用var_export
+ include
,因此每个缓存文件也会自动在OpCache或APC(如果您已为项目配置了指令缓存)中缓存。这是单服务器应用的完美选择,但如果您有多服务器应用,则可能需要在服务器之间共享页面缓存内容。在这种情况下,您可以使用任何PSR-16兼容的缓存适配器,如Memcached或Redis,进行基于网络的"键"=>"值"存储。
<?php require_once __DIR__.'/../vendor/autoload.php'; use Naroga\RedisCache\Redis; use Predis\Client; $config = array( 'scheme' => 'tcp', 'host' => 'localhost', 'port' => 6379 ); $redis = new Redis(new Client($config)); $cache = new PageCache\PageCache(); $cache->setCacheAdapter($redis); $cache->init(); //rest of your PHP page code, everything below will be cached
PageCache还具备"Dry Run模式"选项,默认情况下是关闭的。Dry Run模式启用了所有功能,除了用户不会获取缓存内容,而是获取实时内容。如果启用Dry Run模式,则不会发送缓存头信息和缓存内容。但缓存将像禁用Dry模式一样保存。这种模式允许PageCache进行干运行,如果您想测试、调试或查看缓存内容如何填充,则非常有用。
更多示例,请参阅PageCache示例目录中的代码。
对于那些好奇的人,缓存将保存到配置文件中指定的路径或使用API指定的路径,位于基于文件哈希的目录中。根据文件名的哈希值,将创建2个子目录(如果尚未创建),这是为了避免单个缓存目录中有许多文件。
缓存策略
PageCache使用各种策略来区分同一页面的不同版本。
所有PageCache策略都支持会话。请参阅PageCache带有会话的缓存页面示例。
DefaultStrategy()
是PageCache的默认行为。它使用以下PHP代码缓存页面和生成的缓存文件名
md5($_SERVER['REQUEST_URI'] . $_SERVER['SCRIPT_NAME'] . $_SERVER['QUERY_STRING'] . $session_str)`
字符串$session_str
是序列化后的$_SESSION变量,一些键根据是否启用了会话支持或调用了sessionExclude()而忽略或不包括。
您可以创建自己的命名策略并将其传递给PageCache
$cache = new PageCache\PageCache(); $cache->setStrategy(new MyOwnStrategy());
PageCache包含基于Mobile_Detect的MobileStrategy()
。如果您需要在不同设备上提供不同的URL,则非常有用。请参阅cache_mobiledetect.php PageCache示例文件,了解使用MobileDetect的演示。
您可以根据应用需求定义自己的命名策略。
配置文件
尽管不是必需的,但可以在PageCache初始化时指定配置文件,以指定系统级缓存属性
// Optional system-wide cache config use PageCache\PageCache; $config_file_ = __DIR__.'/config.php'; $cache = new PageCache($config_file_); // You can overwrite or get configuration parameters like this: $cache->config()->getFileLock(); $cache->config()->setUseSession(true);
所有可用配置选项在config文件中有文档说明。请务必查看。
API - PageCache访问方法
以下是从您的应用程序中可以调用的PageCache类的公共方法。这不是一个完整的列表。请查看示例和源代码以获取更多信息。
请检查源代码以获取更多可用方法。
使用会话缓存页面(即启用用户登录的应用程序)
PageCache使在PHP中维护完整页面缓存变得简单,同时使用会话。
使用会话功能的示例之一可能是在您需要将登录用户纳入应用程序时。在这种情况下,URL可能保持不变(如果您依赖会话来登录用户),而页面的内容将为每个登录用户而不同。
为了让PageCache能够识别您的$_SESSION,您必须在配置文件或PHP文件中启用会话支持。在您的PHP文件中,在调用init()
之前,调用$cache->config()->setUseSession(true)
。就是这样!现在,您的会话页面将根据不同的会话值分别缓存。
另一个实用的方法是config()->setSessionExcludeKeys()
。查看会话排除键示例代码。
何时使用config()->setSessionExcludeKeys()
:例如,假设您的应用程序更改了$_SESSION['count']变量,但这不会反映在页面内容中。排除此变量,否则PageCache将为$_SESSION['count']会话变量的每个值生成单独的缓存文件。
// ... $cache->config()->setUseSession(true); $cache->config()->setSessionExcludeKeys(array('count')); // ... $cache->init();
HTTP头部
PageCache可以发送与缓存相关的HTTP头部。这有助于浏览器更快地加载页面,并使应用程序具备SEO意识。搜索引擎将读取这些头部,并知道页面是否已修改或过期。
默认情况下,HTTP头部是禁用的。您可以通过调用config()->setSendHeaders(true)
在init()
之前启用适当的头部,或在配置文件中使用send_headers = true
。尽管默认情况下是禁用的,但我们鼓励您使用此功能。在部署到实时版本之前,在本地应用程序中进行测试。
当启用HTTP头部时,PageCache将尝试在每个响应中自动发送以下HTTP头部
Last-Modified
Expires
ETag
HTTP/1.1 304 Not Modified
PageCache将尝试在缓存内容中发送HTTP/1.1 304 Not Modified
头部。当发送此头部时,内容将不会包含在响应中。这使得您的应用程序速度极快。当存在此头部时,浏览器负责从本地缓存中检索版本。
配置中还有forward_headers
选项,或config()->setForwardHeaders(true)
,允许PageCache从应用程序响应中获取这些HTTP头部的值并将它们存储到缓存项中,以便缓存这些头部。如果您的应用程序具有精细的控制,则此方法很有用。
查看HTTP头部演示的代码。
缓存洪水(狗群效应)保护
在高负载下或当对已过期的同一URL进行多个调用时,可能会发生系统无响应或所有客户端都尝试重新生成同一页面的缓存的情况。这种现象称为缓存洪水。
PageCache使用2种策略来对抗缓存洪水,即使有成千上万的请求被发送到同一页面,系统也能继续正常工作。
文件锁机制
默认情况下,PageCache将文件锁设置为LOCK_EX | LOCK_NB
,这意味着只有一个进程可以写入缓存文件。在此写入期间,所有其他进程将读取旧版本文件。这确保了不会向任何用户展示空白页面,并且所有用户都会收到URL的缓存版本——没有例外。
这消除了缓存洪水的可能性,因为即使有成千上万的客户端在页面已过期时访问页面(一个客户端生成新的缓存并看到新的缓存版本,其余的将收到旧版本),也只有一个文件可以进行写入。通过随机早期和晚期过期,当页面过期时击中页面的用户数量也减少了。
随机早期和晚期过期
使用随机对数计算,有时结果为负,有时为正,同一时间访问相同URL的每个客户的缓存过期时间将不同。虽然一些客户可能仍然获得与其他客户相同的缓存过期值,但缓存过期值在客户之间的整体分布是随机的。
将页面随机过期时间设置为实际页面缓存过期前或后最多6秒,可以确保尝试重新生成缓存内容的客户数量减少。文件锁定已处理缓存页面的同时写入,随机过期进一步减少了此类所需尝试的数量。
以一个例子来说明,假设您已将过期时间设置为10分钟 config()->setCacheExpirationInSeconds(600)
。对于一些客户,页面将在594秒后过期,对于一些客户将在606秒后过期,而对于一些客户将在600秒后过期。实际页面过期时间将在594秒到606秒(包括两端)之间,这是随机计算的。过期值在内部不是整数,因此随机过期值比您想象的要多得多。
就这些了!
请查看PageCache示例文件夹以获取示例代码。