rikudou/psr6-dynamo-db

使用 AWS DynamoDB 实现的 PSR-6 和 PSR-16 缓存实现

v3.1.0 2023-04-27 11:14 UTC

README

Tests Coverage Status Download

用于在 DynamoDB 中存储缓存的库,实现了 PSR-6 和 PSR-16 接口。另请参阅该库的 Symfony 扩展包

从版本 2 开始,该库使用轻量级的 async-aws/dynamo-db,而不是完整的 AWS SDK。

安装

composer require rikudou/psr6-dynamo-db

用法

用法非常简单,你只需要在构造函数中定义详细信息,然后像使用任何其他 PSR-6 或 PSR-16 实现一样使用它

<?php

use Rikudou\DynamoDbCache\DynamoDbCache;
use Rikudou\DynamoDbCache\DynamoDbCacheBuilder;
use AsyncAws\DynamoDb\DynamoDbClient;

$cache = new DynamoDbCache('dynamoTableName', new DynamoDbClient([]));

// with custom field names
$cache = new DynamoDbCache('dynamoTableName', new DynamoDbClient([]), 'customPrimaryKeyField', 'customTtlField', 'customValueField');

// using builder
$cache = DynamoDbCacheBuilder::create('dynamoTableName', new DynamoDbClient([]))
    ->withPrimaryField('customPrimaryKeyField')
    ->withTtlField('customTtlField')
    ->withValueField('customValueField')
    ->build();

建议使用构建器来创建新实例。构建器是不可变的,每个方法都返回一个新的实例。

字段的默认值如下

  • 主键 - id (字符串)
  • ttl 字段 - ttl (数字)
  • 值字段 - value (字符串)

在使用此库之前,你必须创建 DynamoDB 表。

基本示例

<?php
use AsyncAws\DynamoDb\DynamoDbClient;
use Rikudou\DynamoDbCache\DynamoDbCache;

function get(string $key): string
{
    $dynamoDbClient = new DynamoDbClient([
        'region' => 'eu-central-1',
    ]);
    $cache = new DynamoDbCache('cache', $dynamoDbClient); // the default field names are used - id, ttl and value
    
    $item = $cache->getItem($key);
    if ($item->isHit()) {
        return $item->get();    
    }

    // do something to fetch the item
    $result = '...';

    $item->set($result);
    $item->expiresAfter(3600); // expire after one hour
    if (!$cache->save($item)) {
        throw new RuntimeException('Could not save cache');
    }

    return $result;
}

使用 PSR-16 接口的示例

<?php

use AsyncAws\DynamoDb\DynamoDbClient;
use Rikudou\DynamoDbCache\DynamoDbCache;

function get(string $key): string
{
    $dynamoDbClient = new DynamoDbClient([
        'region' => 'eu-central-1',
    ]);
    $cache = new DynamoDbCache('cache', $dynamoDbClient); // the default field names are used - id, ttl and value

    $value = $cache->get($key);
    if ($value !== null) {
        return $value;
    }
    
    // do something to fetch the item
    $result = '...';

    if (!$cache->set($key, $result, 3600)) {
       throw new RuntimeException('Could not save cache');     
    }
    
    return $result;
}

前缀

你可以使用前缀配置自动为 DynamoDB 中的所有键添加前缀,如下所示

<?php

use Rikudou\DynamoDbCache\DynamoDbCacheBuilder;
use AsyncAws\DynamoDb\DynamoDbClient;

$cache = DynamoDbCacheBuilder::create('myTable', new DynamoDbClient([]))
    ->withPrefix('myCustomPrefix#')
    ->build();

$item = $cache->getItem('key1'); // fetches an item with key myCustomPrefix#key1
$key = $item->getKey(); // $key holds the full key including prefix, myCustomPrefix#key1

转换器

此实现通过使用转换器支持所有 \Psr\Cache\CacheItemInterface 实例,这些转换器将对象转换为 \Rikudou\DynamoDbCache\DynamoCacheItem。请注意,转换过程中可能会丢失一些信息,特别是过期日期。

你可以为你的特定类编写自己的转换器,并包括对过期日期的支持,如下所示

<?php

use Rikudou\DynamoDbCache\Converter\CacheItemConverterInterface;
use Psr\Cache\CacheItemInterface;
use Rikudou\DynamoDbCache\DynamoCacheItem;
use Rikudou\Clock\Clock;
use Rikudou\DynamoDbCache\Encoder\SerializeItemEncoder;

class MyCacheItemConverter implements CacheItemConverterInterface
{
    /**
     * If this methods returns true, the converter will be used
     */
    public function supports(CacheItemInterface $cacheItem): bool
    {
        return $cacheItem instanceof MyCacheItem;
    }
    
    public function convert(CacheItemInterface $cacheItem): DynamoCacheItem
    {
        assert($cacheItem instanceof MyCacheItem);
        return new DynamoCacheItem(
            $cacheItem->getKey(),
            $cacheItem->isHit(),
            $cacheItem->get(),
            $cacheItem->getExpirationDate(), // this is a custom method from the hypothetical MyCacheItem
            new Clock(),
            new SerializeItemEncoder()
        );
    }
}

然后你需要将其注册到转换器中,并将转换器分配给缓存

<?php

use Rikudou\DynamoDbCache\Converter\CacheItemConverterRegistry;
use Rikudou\DynamoDbCache\DynamoDbCache;
use AsyncAws\DynamoDb\DynamoDbClient;
use Rikudou\DynamoDbCache\DynamoDbCacheBuilder;

// you don't need to add the default one as well, it will be added automatically if it's missing
$converter = new CacheItemConverterRegistry(new MyCacheItemConverter());
$dynamoClient = new DynamoDbClient([]);
$cache = DynamoDbCacheBuilder::create('myTable', $dynamoClient)
    ->withConverterRegistry($converter)
    ->build();

$myOldCache = new MyCacheImplementation();
$cacheItem = $myOldCache->getItem('test'); // this is now an instance of MyCacheItem

// your custom converter will get used to convert it to DynamoCacheItem
// if you didn't supply your own converter, the \Rikudou\DynamoDbCache\Converter\DefaultCacheItemConverter
// would be used and the information about expiration date would be lost
$cache->save($cacheItem);

编码器

默认情况下,值使用 PHP 序列化器进行序列化。如果你想与其他语言的程序(或不同 PHP 程序,这些程序没有相同的类)共享缓存,你可以使用 \Rikudou\DynamoDbCache\Encoder\JsonItemEncoder 或编写自己的。

注意:JsonItemEncoder 在处理对象时会丢失信息,如果你需要存储对象信息,这个编码器可能不适合你。另一方面,如果你只存储标量数据或数组,JsonItemEncoder 就足够了。

使用 JsonItemEncoder 的示例

<?php
use Rikudou\DynamoDbCache\DynamoDbCache;
use Rikudou\DynamoDbCache\DynamoDbCacheBuilder;
use AsyncAws\DynamoDb\DynamoDbClient;
use Rikudou\DynamoDbCache\Encoder\JsonItemEncoder;

$encoder = new JsonItemEncoder(); // with default flags and depth
$encoder = new JsonItemEncoder(JSON_PRETTY_PRINT, JSON_THROW_ON_ERROR, 100); // with custom encode and decode flags and depth

$cache = DynamoDbCacheBuilder::create('myTable', new DynamoDbClient([]))
    ->withEncoder($encoder)
    ->build();

现在你的值将以 JSON 编码的形式保存到 DynamoDB 中。

编写自己的编码器很简单,你只需要实现 \Rikudou\DynamoDbCache\Encoder\CacheItemEncoderInterface 接口

<?php

use Rikudou\DynamoDbCache\Encoder\CacheItemEncoderInterface;

class MyEncoder implements CacheItemEncoderInterface
{
    /**
     * @param mixed $input
     * @return string
     */
    public function encode($input) : string
    {
        // TODO: Implement encode() method.
    }

    /**
     * @param string $input
     * @return mixed
     */
    public function decode(string $input)
    {
        // TODO: Implement decode() method.
    }
}