hyperf/docs/en/cache.md
Deeka Wong 2e709cda58
Added MemoryDriver for hyperf/cache (#6542)
Co-authored-by: 张城铭 <z@hyperf.io>
2024-02-20 11:22:01 +08:00

8.5 KiB

Cache

hyperf/cache provides aspect cache based on Aspect implementation, and also provides cache classes that implement Psr\SimpleCache\CacheInterface.

Install

composer require hyperf/cache

Default allocation

Configuration Default value Remark
driver Hyperf\Cache\Driver\RedisDriver Cache driver, default is Redis
packer Hyperf\Codec\Packer\PhpSerializerPacker Packager
prefix c: Cache prefix
skip_cache_results [] Certain results are not cached
<?php

return [
    'default' => [
        'driver' => Hyperf\Cache\Driver\RedisDriver::class,
        'packer' => Hyperf\Codec\Packer\PhpSerializerPacker::class,
        'prefix' => 'c:',
        'skip_cache_results' => [],
    ],
];

Use

Simple Cache method

Simple Cache is the PSR-16 specification. This component adapts to the specification. If you want to use Psr\SimpleCache\CacheInterface Cache class, for example, if you want to rewrite the cache module of EasyWeChat, you can get Psr\SimpleCache\CacheInterface directly from the dependency injection container, as shown below:


$cache = $container->get(\Psr\SimpleCache\CacheInterface::class);

Annotation method

The component provides Hyperf\Cache\Annotation\Cacheable annotation, which acts on class methods and can configure the corresponding cache prefix, expiration time, listener and cache group. For example, UserService provides a user method that can query user information corresponding to id. When the Hyperf\Cache\Annotation\Cacheable annotation is added, the corresponding Redis cache will be automatically generated. The key value is user:id and the timeout is 9000 seconds. When querying for the first time, it will be checked from the database, and for subsequent queries, it will be checked from the cache.

<?php

namespace App\Services;

use App\Models\User;
use Hyperf\Cache\Annotation\Cacheable;

class UserService
{
    #[Cacheable(prefix: "user", ttl: 9000, listener: "user-update")]
    public function user($id)
    {
        $user = User::query()->where('id',$id)->first();

        if($user){
            return $user->toArray();
        }

        return null;
    }
}

Clean the cache generated by #[Cacheable]

We provide two annotations, CachePut and CacheEvict, to implement cache update and cache clearing operations.

Of course, we can also delete the cache through events. Let's create a new Service to provide a method to help us handle caching.

However, we recommend users to use annotation processing instead of listeners.

<?php

declare(strict_types=1);

namespace App\Service;

use Hyperf\Di\Annotation\Inject;
use Hyperf\Cache\Listener\DeleteListenerEvent;
use Psr\EventDispatcher\EventDispatcherInterface;

class SystemService
{
    #[Inject]
    protected EventDispatcherInterface $dispatcher;

    public function flushCache($userId)
    {
        $this->dispatcher->dispatch(new DeleteListenerEvent('user-update', [$userId]));

        return true;
    }
}

When we customize the value of Cacheable, such as the following situation.

<?php

declare(strict_types=1);

namespace App\Service\Cache;

use Hyperf\Cache\Annotation\Cacheable;

class DemoService
{

    #[Cacheable(prefix: "cache", value: "_#{id}", listener: "user-update")]
    public function getCache(int $id)
    {
        return $id . '_' . uniqid();
    }
}

You need to modify the $arguments variable in the DeleteListenerEvent constructor accordingly. The specific code is as follows.

<?php

declare(strict_types=1);

namespace App\Service;

use Hyperf\Di\Annotation\Inject;
use Hyperf\Cache\Listener\DeleteListenerEvent;
use Psr\EventDispatcher\EventDispatcherInterface;

class SystemService
{
    #[Inject]
    protected EventDispatcherInterface $dispatcher;

    public function flushCache($userId)
    {
        $this->dispatcher->dispatch(new DeleteListenerEvent('user-update', ['id' => $userId]));

        return true;
    }
}

Introduction to annotations

Cacheable

For example, in the following configuration, the cache prefix is user, the timeout is 7200, and the deletion event name is USER_CACHE. The corresponding cache KEY is generated as c:user:1.

<?php

declare(strict_types=1);

namespace App\Service;

use App\Models\User;
use Hyperf\Cache\Annotation\Cacheable;

class UserService
{
    #[Cacheable(prefix: "user", ttl: 7200, listener: "USER_CACHE")]
    public function user(int $id): array
    {
        $user = User::query()->find($id);

        return [
            'user' => $user->toArray(),
            'uuid' => $this->unique(),
        ];
    }
}

When value is set, the framework will cache the KEY key naming according to the set rules. In the following example, when $user->id = 1, the cached KEY is c:userBook:_1

This configuration also supports other types of cache annotations described below

<?php

declare(strict_types=1);

namespace App\Service;

use App\Models\User;
use Hyperf\Cache\Annotation\Cacheable;

class UserBookService
{
    #[Cacheable(prefix: "userBook", ttl: 6666, value: "_#{user.id}")]
    public function userBook(User $user): array
    {
        return [
            'book' => $user->book->toArray(),
            'uuid' => $this->unique(),
        ];
    }
}

CacheAhead

For example, in the following configuration, the cache prefix is user, the timeout is 7200, the corresponding cache KEY generated is c:user:1, and the cache is initialized every 10 seconds from 7200 to 600 seconds until the first time success.

<?php

declare(strict_types=1);

namespace App\Service;

use App\Models\User;
use Hyperf\Cache\Annotation\CacheAhead;

class UserService
{
    #[CacheAhead(prefix: "user", ttl: 7200, aheadSeconds: 600, lockSeconds: 10)]
    public function user(int $id): array
    {
        $user = User::query()->find($id);

        return [
            'user' => $user->toArray(),
            'uuid' => $this->unique(),
        ];
    }
}

CachePut

CachePut is different from Cacheable in that it executes the function body every time it is called and then rewrites the cache. So when we want to update the cache, we can call the relevant methods.

<?php

declare(strict_types=1);

namespace App\Service;

use App\Models\User;
use Hyperf\Cache\Annotation\CachePut;

class UserService
{
    #[CachePut(prefix: "user", ttl: 3601)]
    public function updateUser(int $id)
    {
        $user = User::query()->find($id);
        $user->name = 'HyperfDoc';
        $user->save();

        return [
            'user' => $user->toArray(),
            'uuid' => $this->unique(),
        ];
    }
}

CacheEvict

CacheEvict is easier to understand. When the method body is executed, the cache will be actively cleaned.

<?php

declare(strict_types=1);

namespace App\Service;

use Hyperf\Cache\Annotation\CacheEvict;

class UserBookService
{
    #[CacheEvict(prefix: "userBook", value: "_#{id}")]
    public function updateUserBook(int $id)
    {
        return true;
    }
}

Cache driver

Redis driver

Hyperf\Cache\Driver\RedisDriver will store cache data in Redis, and users need to configure the corresponding Redis configuration. This mode is the default mode.

Process memory driver

If you need to cache data into memory, you can try this driver. The configuration is as follows:

<?php

return [
    'memory' => [
        'driver' => Hyperf\Cache\Driver\MemoryDriver::class,
    ],
];

Coroutine memory driver

If you need to cache data into Context, you can try this driver. For example, in the following application scenario, Demo::get will be called multiple times in multiple places, but you do not want to query Redis every time.

<?php
use Hyperf\Cache\Annotation\Cacheable;

class Demo
{    
    public function get($userId, $id)
    {
        return $this->getArray($userId)[$id] ?? 0;
    }

    #[Cacheable(prefix: "test", group: "co")]
    public function getArray(int $userId): array
    {
        return $this->redis->hGetAll($userId);
    }
}

The corresponding configuration is as follows:

<?php

return [
    'co' => [
        'driver' => Hyperf\Cache\Driver\CoroutineMemoryDriver::class,
        'packer' => Hyperf\Codec\Packer\PhpSerializerPacker::class,
    ],
];