bbc / ipr-cache
一个简单的缓存包装器,利用 Doctrine Cache 实现模糊和过时重验证缓存。
Requires
- php: >=5.5
- doctrine/cache: ^1.3
Requires (Dev)
- phpunit/phpunit: ^4.0
- squizlabs/php_codesniffer: ^2.0
This package is not auto-updated.
Last update: 2024-09-14 19:36:40 UTC
README
围绕 Doctrine\Cache 的简单缓存包装器,允许我们进行标准、模糊和过时重验证缓存。
要求
- PHP >= 5.5
- Doctrine/Cache 理解的缓存后端
使用方法
入门
通过 Composer 安装
$ composer require bbc/ipr-cache
现在你需要构造一个 BBC\iPlayerRadio\Cache
实例,并将一个 Doctrine\Common\Cache\Cache
实例传递给它。以下是一个简单的示例
$cacheAdapter = new Doctrine\Common\Cache\ArrayCache(); $cache = new BBC\iPlayerRadio\Cache\Cache($cacheAdapter);
$cacheAdapter
是实际进行缓存读写的东西,我们的库只是将其包装起来。因此,我们接受任何 Doctrine\Common\Cache\*
类,所以如果你使用 Redis、Memcached,甚至是普通的文件系统缓存,这个库都将与它一起工作。(更具体地说,任何实现了 Doctrine\Common\Cache\Cache
接口的类都被接受)。
读取项目
这个库在读取缓存项的方式上与您可能习惯的不同。Doctrine 使用的是“传统”方式,工作原理大致如下
if (($item = $cache->fetch($cacheKey) === false) { $data = '{somekey: somevalue}'; $cache->save($cacheKey, $data, $lifetime); }
这对于简单用例来说非常好,但是如果您想干净地执行模糊和过时重验证等操作,这种 API 风格会很快变得笨拙。
因此,这个库使用存储库模式来读取和写入缓存项。这使得我们能够更干净地封装不同缓存模式的逻辑,即使一开始看起来可能有些奇怪!
以下是读取缓存项的方式
<?php use BBC\iPlayerRadio\Cache\Cache; use Doctrine\Common\Cache\ArrayCache; $cacheAdapter = new ArrayCache(); $cache = new Cache($cacheAdapter); $cacheItem = $cache->get('cache_key'); // $cacheItem is an instance of BBC\iPlayerRadio\Cache\CacheItem and will be an object whether the item // is present in cache or not. You can now call functions on this object to ascertain its state: // Check if the item is expired: true if it isn't in the cache, false if it is var_dump($cacheItem->isExpired()); // Retrieve the data you stored in the cache from the item: $cacheData = $cacheItem->getData();
如果您真的想,可以自己构造一个 BBC\iPlayerRadio\Cache\CacheItem
实例,但最简单的方法是直接调用 $cache->get('myCacheKey');
,因为它会始终返回一个 CacheItem 实例,无论它是否存在于缓存中。
“模糊”缓存
假设您有一个需要执行五个操作来构建自己的页面部分。如果我们将所有这些操作都缓存相同的时间长度,它们将同时过期,可能会在服务尝试一次性重建所有内容时造成过载。一种缓解方法是对缓存生命周期进行“模糊”;从任何生命周期中添加或减去一个随机数,以确保事物以更分散的方式过期。
这是该库的默认操作模式,所有您的缓存时间都将被“模糊”±5%,以防止工程化踩踏。以下是使用方法
use BBC\iPlayerRadio\Cache\Cache; use Doctrine\Common\Cache\ArrayCache; $cache = new Cache(new ArrayCache()); // Attempt to read from the cache: $item = $cache->get('hello_world'); if ($item->isExpired()) { // We don't have an item in the cache, let's rebuild! $data = 'This could be the result of an expensive call...'; // Now we update the item we fetched from the cache with the data // and give it a new expiry time (in seconds). $item->setData($data); $item->setLifetime(60); // And re-store in the cache: $cache->save($item); } // Now we can make use of that data: echo $item->getData();
如您所见,缓存会自动处理模糊。
默认模糊为5%,但您可以使用 setFuzz() 进行更改
$item = $cache->get('hello_world'); $item->setData('I am data!'); $item->setFuzz(0.1); // 10% fuzz $cache->save($item);
“纯”缓存
如果您不想模糊生命周期,怎么办?很简单,您可以使用“纯”缓存,这实际上就是关闭模糊
use BBC\iPlayerRadio\Cache\Cache; use Doctrine\Common\Cache\ArrayCache; $cache = new Cache(new ArrayCache()); // Attempt to read from the cache: $item = $cache->get('hello_world'); if ($item->isExpired()) { $data = 'This could be the result of an expensive call...'; $item->setData($data); $item->setLifetime(60); // This is the only difference from Fuzzy caching, we set the fuzz to 0: $item->setFuzz(0); $cache->save($item); }
过时重验证缓存
过时重验证缓存(有时也称为“软”缓存)为对象引入了两个不同的生命周期;它的最佳食用期和它的过期时间。
一旦一个项超过了它的最佳食用期(并变为“过时”),客户端应该尝试重建数据。但是,如果重建失败,它们可以使用过时的数据继续操作。
但是过期日期与其他两种模式的工作方式相同,到那时它将从缓存后端清除,并且您必须重建数据或优雅地降级。
以下是使用过时重验证缓存的方法
use BBC\iPlayerRadio\Cache\Cache; use Doctrine\Common\Cache\ArrayCache; $cache = new Cache(new ArrayCache()); $item = $cache->get('hello_world'); if ($item->isStale() || $item->isExpired()) { $data = someExpensiveOperation(); if ($data) { // We got a good response, let's cache that: $item->setData($data); $item->setBestBefore(60); // start re-fetching after 1 minute $item->setLifetime(300); // flush from cache at 5 minutes $cache->save($item); } } // At this point, we have to re-examine the cache item to see if the data has been updated. The $item could actually // be in any state at this point: // // - The item was in cache and valid, no refetch happened, you're good to go // - The item was stale, we re-fetched successfully, you're good to go // - The item was stale, re-fetch failed, go with the stale data // - The item was expired, re-fetch failed, you need to do something // // Luckily, these four complex states can be handled simply by asking if the item is expired or not: if ($item->isExpired()) { // We have no data to work with: gracefullyDegrade(); } else { // We have data from somewhere; use it! echo $item->getData(); }
过时重验证缓存和模糊
如果您选择通过调用setBestBefore()使用软缓存,模糊处理将应用于最佳使用时间,而不是过期时间。这又是为了防止您的应用开始重新请求数据时出现拥挤。
缓存前缀
新增于v1.1.0
Cache类可以无痕地为所有您提供的缓存键添加一个额外的前缀。当您知道有两个应用将以相似的关键字(例如md5字符串)写入缓存后端时,这很有用。
// You can either pass the prefix into the constructor: $cache = new Cache($adapter, 'myprefix_'); // Or set it explicitly: $cache->setPrefix('mycache_');
您永远不需要再次使用前缀;读取、写入和删除对象都像没有前缀一样发生,Cache类内部处理所有这些。
$cache = new Cache($adapter, 'myprefix_'); $item = $cache->get('todays_weather'); // actually reads: 'myprefix_todays_weather' $item->setData('Cloudy'); $cache->save($item); $cache->delete('yesterdays_weather'); // actually removes 'myprefix_yesterdays_weather' if ($cache->hasKey('todays_weather')) { $item = $cache->get('todays_weather'); echo 'Today it is: '.$item->getData(); }
接口
在传递缓存作为参数时,请针对CacheInterface进行类型提示,而不是显式地针对Cache。
function doSomethingWithCache(BBC\iPlayerRadio\Cache\CacheInterface $cache) { };
如果您想自己实现缓存项,自然也有BBC\iPlayerRadio\Cache\CacheItemInterface
。
模拟缓存
您可以通过使用Doctrine的ArrayCache适配器轻松地在单元测试中拥有一个模拟缓存实例。这正是我们测试Cache类本身的方式;
$mockedCache = new Cache(new ArrayCache());