thamtech/yii2-refresh-ahead-cache

Yii2的预刷新缓存策略

资助包维护!
Liberapay

安装: 31

依赖项: 0

建议者: 0

安全性: 0

星标: 0

关注者: 2

分支: 0

公开问题: 0

类型:yii2-extension

v0.3.6 2020-08-25 22:05 UTC

This package is auto-updated.

Last update: 2024-09-10 03:54:35 UTC


README

Yii2预刷新缓存可以装饰Yii2缓存组件或其他组件以实现预刷新缓存策略。

预刷新缓存策略(也称为预读取)用于在数据过期之前刷新缓存数据。通过在数据过期之前(以异步方式进行)刷新缓存数据,最终用户永远不需要忍受刷新的延迟。此外,它还可以帮助避免缓存踩踏

有关许可证信息,请参阅LICENSE文件。

安装

安装此扩展的首选方式是通过composer

php composer.phar require --prefer-dist thamtech/yii2-read-ahead-cache

或者添加

"thamtech/yii2-read-ahead-cache": "*"

到您的composer.json文件的require部分。

使用方法

装饰缓存组件

您可以通过附加RefreshAheadCacheBehavior来将预刷新功能添加到应用程序的缓存组件。例如,在您的应用程序配置中

<?php
[
    'components' => [
        'cache' => [
            'class' => 'yii\redis\Cache',
            'as refreshAhead' => 'thamtech\caching\refreshahead\RefreshAheadCacheBehavior',
        ],
    ],
];

如果将行为声明为配置数组,您可以选择配置以下参数

<?php
[
    'components' => [
        'redisCache' => [
            'class' => 'yii\redis\Cache',
        ],
        'appMutex' => [
            'class' => 'yii\redis\Mutex',
        ],
        'cache' => [
            'class' => 'yii\caching\FileCache',
            'as refreshAhead' => [
                'class' => 'thamtech\caching\refreshahead\RefreshAheadCacheBehavior',
                'refreshTimeoutCache' => 'redisCache',
                'refreshAheadFactor' => 0.5,
                'refreshTimeoutKeySuffix' => 'refresh-ahead-timeout',
                'mutex' => 'appMutex',
            ],
        ],
    ],
];

装饰任何组件

默认情况下,RefreshAheadCacheBehavior假设其所有者(作为行为附加到的组件)是一个缓存组件,它将用于存储缓存数据以及存储刷新超时密钥。但是,在这种情况下,您可以指定要使用的缓存组件,因此您不需要将RefreshAheadCacheBehavior附加到缓存组件。您可以将它附加到任何Yii 组件,前提是您在行为配置中指定要使用的缓存组件。

<?php
$dataManager = Yii::createObject([
    'class' => DataManager::class,
    'as refreshAhead' => [
        'class' => 'thamtech\caching\refreshahead\RefreshAheadCacheBehavior',
        
        // use application 'cache' component for data storage
        'dataCache' => 'cache',
        
        // use application 'cache' component for refresh timeout key storage
        'refreshTimeoutCache' => 'cache',
    ],
]);

如果数据值和刷新超时键将存储在同一个缓存组件中,则可以将单个cache属性设置为设置dataCacherefreshTimeoutCache的快捷方式。以下配置与上述配置等价

<?php
$dataManager = Yii::createObject([
    'class' => DataManager::class,
    'as refreshAhead' => [
        'class' => 'thamtech\caching\refreshahead\RefreshAheadCacheBehavior',
        
        // use application 'cache' component for both data storage and refresh
        // timeout key storage
        'cache' => 'cache',
    ],
]);

getOrSet的嵌入式替换

RefreshAheadCacheBehavior向缓存或它装饰的任何其他组件添加了一个getRefreshOrSet()方法。该方法具有与getOrSet()相同的签名,因此您可以在当前使用getOrSet()的地方进行嵌入式替换。例如,

<?php
$data = $cache->getOrSet($key, function ($cache) {
    return $this->calculateSomething();
});

// drop-in replacement:
$data = $cache->getRefreshOrSet($key, function ($cache) {
    return $this->calculateSomething();
});


// with specified $duration and $dependency
$data = $cache->getOrSet($key, function ($cache) {
    return $this->calculateSomething();
}, $duration, $dependency);

// drop-in replacement
$data = $cache->getRefreshOrSet($key, function ($cache) {
    return $this->calculateSomething();
}, $duration, $dependency);

预刷新策略尝试在刷新超时缓存组件中添加一个刷新超时键,其持续时间短于请求的$duration(默认为$duration的一半)。如果添加成功,则意味着任何以前的刷新超时键已过期,缓存数据需要刷新。

当使用单个可调用参数调用getRefreshOrSet()时,预刷新策略调用该可调用项,使用指定的$duration将返回值存储到数据缓存组件中,并返回该值(在上面的示例中为$data变量)。

另一方面,如果添加刷新超时键尝试失败,这意味着该键已经存在且未过期,因此目前不需要刷新。预刷新策略使用$key在数据缓存组件中查找缓存的值,如果找到则返回它。如果没有在数据缓存中找到(可能是缓存已刷新或键已被移除),则调用可调用项来计算新值。使用指定的$duration在新值设置在数据缓存组件中,并返回该值。

典型用法

如果你能支持异步刷新,则可以使用法进一步改进。为了做到这一点,我们必须向预刷新策略提供两个可调用项:一个用于异步触发刷新,另一个用于同步刷新数据和返回结果。

用法与上述示例类似,但getRefreshOrSet()的第二个参数将是一个GeneratorInterface对象或配置数组,而不是单个可调用项。例如,

<?php
$data = $cache->getRefreshOrSet($key, [
    // called by Refresh Ahead strategy if the data is still cached, but it is
    // time to refresh it
    'refresh' => function ($cache) {
        // queue the refresh task to be run at a later time
        // return `true` when task is queued, `false` if the task was not queued
        // (in which case the `refresh` callable will be called again in a
        // subsequent request)
        return $this->taskQueue->append('calculateSomething');
    },
    
    // called by Refresh Ahead strategy if the data is not in cache (it may
    // have expired before it could be refreshed, or it could have been
    // flushed or evicted, etc.)
    'generate' => function ($cache) {
        return $this->calculateSomething();
    }
], $duration, $dependency);

如果你在RefreshAheadCacheBehavior中配置了一个mutex组件,你可以使用mutexLockTimeout属性指定获取锁的超时时间

<?php
$generator = [
    'refresh' => function ($cache) {
        return $this->taskQueue->append('calculateSomething');
    },
    'generate' => function ($cache) {
        return $this->calculateSomething();
    },
    // Attempt to acquire a mutex lock for 12 seconds before invoking
    // the 'generate' callback. If the lock is acquired, Refresh Ahead will
    // check to see if the value is in the data cache once more before invoking
    // 'generate', in case another process was generating and caching the value
    // already.
    'mutexLockTimeout' => 12,
];

$data = $cache->getRefreshOrSet($key, $generator, $duration, $dependency);

通过在行为上配置一个mutex组件并将mutexLockTimeout作为生成器上的属性设置,预刷新策略将尝试获取锁以调用generate可调用项。这样,如果多个请求在值已过期时几乎同时到达(一个缓存竞争),第一个获取锁的过程将计算值并将其存储在缓存中。其他进程等待锁释放。一旦第一个过程释放了锁,值已被计算并存储在缓存中,因此其他进程将在缓存中检查它,找到它,并返回它而无需调用generate可调用项。

如果你的任务队列可以异步运行,例如在cron任务中,你可以在generateAndSet()的调用中使用相同的$generator来完成刷新过程并在后台更新缓存值。例如,

<?php
// using the same parameters defined in the previous example:
$data = $cache->generateAndSet($key, $generator, $duration, $dependency);

这将调用generate可调用项(如果项目尚未由同一时间内的generate的另一个调用缓存),并在返回之前将结果设置在缓存中。

另请参阅