hypef 八、缓存

文档地址:Hyperf

Aspect基于概念AOP(面向切面编程),aop: Hyperf

用通俗的话来讲,就是在 Hyperf 里可以通过 切面(Aspect) 介入到任意类的任意方法的执行流程中去,从而改变或加强原方法的功能,这就是 AOP。

 我理解就像tp中的log类,引入之后可以在任何位置保存日志,或者tp全局自定义函数,比如核心文件中的help.php或者function.php中的函数可以随时随地使用。

psr-16规范:PSR-16: Common Interface for Caching Libraries - PHP-FIG

配置文件

#config/cache.php
return [
    'default' => [
        'driver' => Hyperf\Cache\Driver\RedisDriver::class,//缓存驱动,默认为 Redis
        'packer' => Hyperf\Utils\Packer\PhpSerializerPacker::class,//打包器
        'prefix' => 'c:',//缓存前缀
    ],
];

模块hyperf/cache,使用psr/simple-cache,composer地址:psr/simple-cache - Packagist,仅定义缓存实现接口。

一、自定义实现

1.1 自定义实体类

系统内部实现为Hyperf\Cache\Cache类,该类继承Psr\SimpleCache\CacheInterface。

#Hyperf\Cache\ConfigProvider
public function __invoke(): array
    {
        return [
            'dependencies' => [
                CacheInterface::class => Cache::class,
            ],
           ……
        ];
    }

#Hyperf\Cache\Cache
use Psr\SimpleCache\CacheInterface;

class Cache implements CacheInterface
{……}

 可以通过备注方法使用,下文介绍。

1.2 自定义驱动

默认驱动为Hyperf\Cache\Driver\RedisDriver,继承Hyperf\Cache\Driver\Driver,通过容器获取redis。自定义驱动可以参照该类。

#/config/authload/cache.php
return [
    'default' => [
        'driver' => Hyperf\Cache\Driver\RedisDriver::class,
        'packer' => Hyperf\Utils\Packer\PhpSerializerPacker::class,
        'prefix' => 'c:',
    ],
];

#Hyperf\Cache\Driver\RedisDriver
class RedisDriver extends Driver implements KeyCollectorInterface
{
    /**
     * @var \Redis
     */
    protected $redis;

    public function __construct(ContainerInterface $container, array $config)
    {
        parent::__construct($container, $config);

        $this->redis = $container->get(\Redis::class);
    }
……
}

 以使用框架自带另一个驱动举例。Hyperf\Cache\Driver\CoroutineMemoryDriver 携程内存驱动,重点在于程序的并发执行,例子中调用getCache1()(get()->sleep(2))两次,应该总间隔4秒左右,结果只执行2秒左右。

#/config/autoload/cache.php
return [
    'default' => [
        'driver' => Hyperf\Cache\Driver\RedisDriver::class,
        'packer' => Hyperf\Utils\Packer\PhpSerializerPacker::class,
        'prefix' => 'c:',
    ],
    'test1' => [
        'driver' => Hyperf\Cache\Driver\CoroutineMemoryDriver::class,
        'packer' => Hyperf\Utils\Packer\PhpSerializerPacker::class,
        'prefix' => 'cm:',
    ],
];

# App\Controller\TestController 自定义控制器
 /**
     * @Inject
     * @var Cache1Service
     */
/**
携程缓存
*/
    public function index3()
    {
        $time1 = microtime(true);
        $user = $this->service->getCache1();
        $user2 = $this->service->getCache1();
        $user3 = $this->service->getCache1();
        $time2 = microtime(true);
        $time = $time2 - $time1;
        $data = [
            'userlist' => [
                $user,
                $user2,
                $user3,
            ],
            $time,
        ];
        return $this->response->json($data);
    }

#执行结果
{"userlist":["test_1689584463","test_1689584463","test_1689584463"],"0":2.076417922973633}]

#App\Service\Cache1Service
 /**
  * @Cacheable(prefix="cache", group="test1")
  */
    public function getCache1($user = "test")
    {
        return $this->get($user);
    }

1.3 自定义配置

默认配置,在.env中可以设置默认的redis连接属性,在配置文件中可以设置多个redis链接。

自定义在驱动中处理,所以要加配置需手动重写驱动和缓存配置,主要点在设置redis实例时不适用容器获取,而是使用redis代理创建。

#/config/autoload/redis.php
return [
    'default' => [
        'host' => env('REDIS_HOST', 'localhost'),
        'auth' => env('REDIS_AUTH', null),
        'port' => (int) env('REDIS_PORT', 6379),
        'db' => (int) env('REDIS_DB', 0),
        'pool' => [
            'min_connections' => 1,
            'max_connections' => 10,
            'connect_timeout' => 10.0,
            'wait_timeout' => 3.0,
            'heartbeat' => -1,
            'max_idle_time' => (float) env('REDIS_MAX_IDLE_TIME', 60),
        ],
    ],
];
#/config/autoload/cache.php
……

'test2' => [
        'driver' => App\Service\RedisDriver::class,
        'packer' => Hyperf\Utils\Packer\PhpSerializerPacker::class,
        'prefix' => 'cm:',
    ],
];
……
# App\Service\RedisDriver
class RedisDriver extends hRedisDriver
{

    /**
     * @var \Redis
     */
    protected $redis;
    public function __construct(ContainerInterface $container, array $config)
    {
        parent::__construct($container, $config);

        $this->redis = make(RedisProxy::class, ['pool' => 'test1']);
    }

}

#App\Service\Cache1Service
 /**
     * @Cacheable(prefix="cache", group="test2")
     */
    public function getCache2($user = "test")
    {
        return $this->get($user);
    }


#请求
curl 127.0.0.1:9501/test/index4?user=qwe1
{"user":"qwe1_1689586593"}


#redis
select 1
keys *
1) "cm:cache:qwe1"
get cm:cache:qwe1
"s:15:\"qwe1_1689586593\";"

二、获取缓存接口规范

通过容器获取缓存实体类,对应操作redis的方法在对应驱动中。

$container = Hyperf\Utils\ApplicationContext::getContainer();
//获取缓存 Hyperf\Cache\Cache
$cache = $container->get(\Psr\SimpleCache\CacheInterface::class);
//调用driver方法
# Hyperf\Cache\Cache
 public function __call($name, $arguments)
    {
        return $this->driver->{$name}(...$arguments);
    }

    public function get($key, $default = null)
    {
        return $this->__call(__FUNCTION__, func_get_args());
    }

    public function set($key, $value, $ttl = null)
    {
        return $this->__call(__FUNCTION__, func_get_args());
    }

#Hyperf\Cache\Driver\RedisDriver
 public function get($key, $default = null)
    {
        $res = $this->redis->get($this->getCacheKey($key));
        if ($res === false) {
            return $default;
        }

        return $this->packer->unpack($res);
    }
 public function set($key, $value, $ttl = null)
    {
        $seconds = $this->secondsUntil($ttl);
        $res = $this->packer->pack($value);
        if ($seconds > 0) {
            return $this->redis->set($this->getCacheKey($key), $res, $seconds);
        }

        return $this->redis->set($this->getCacheKey($key), $res);
    }

三、注释

注释通过反射实现方法的调用,有两种注释方法。

/**
*@方法名(参数=值,参数=值)
**/

#[@方法名(参数:值,参数:值)]

 依赖注入通过aop实现,没看通过视频教程大概知道调用。

设置文件之后会在runtime中再生成对应的文件,其中有aop的调用。

大概通过inject设置注入对象到AnnotationCollector类中,被注入对象调用构造再从AnnotationCollector类中获取。

#runtime/container/proxy/app_controller_TestController.proxy.php
class TestController extends AbstractController
{
    use \Hyperf\Di\Aop\ProxyTrait;
    use \Hyperf\Di\Aop\PropertyHandlerTrait;
    function __construct()
    {
        if (method_exists(parent::class, '__construct')) {
            parent::__construct(...func_get_args());
        }
        $this->__handlePropertyHandler(__CLASS__);
    }
……
}

#Hyperf\Di\Aop\PropertyHandlerTrait
trait PropertyHandlerTrait
{
    protected function __handlePropertyHandler(string $className)
    {
        ……
         // Inject the properties of current class
        $handled = $this->__handle($className, $className, $properties);

        // Inject the properties of traits.
        // Because the property annotations of trait couldn't be collected by class.
        $handled = $this->__handleTrait($reflectionClass, $handled, $className);
        ……
    }
 protected function __handle(string $currentClassName, string $targetClassName, array $properties): array
    {
        $handled = [];
        foreach ($properties as $propertyName) {
            $propertyMetadata = AnnotationCollector::getClassPropertyAnnotation($targetClassName, $propertyName);
            if (! $propertyMetadata) {
                continue;
            }
            foreach ($propertyMetadata as $annotationName => $annotation) {
                if ($callbacks = PropertyHandlerManager::get($annotationName)) {
                    foreach ($callbacks as $callback) {
                        call($callback, [$this, $currentClassName, $targetClassName, $propertyName, $annotation]);
                    }
                    $handled[] = $propertyName;
                }
            }
        }

        return $handled;
    }
}

#Hyperf\Di\Annotation\AnnotationCollector
public static function collectProperty(string $class, string $property, string $annotation, $value): void
    {
        static::$container[$class]['_p'][$property][$annotation] = $value;
    }
 public static function getClassPropertyAnnotation(string $class, string $property)
    {
        return static::get($class . '._p.' . $property);
    }

#Hyperf\Di\Annotation\Inject
class Inject extends AbstractAnnotation
{
public function collectProperty(string $className, ?string $target): void
    {
        try {
            $reflectionClass = ReflectionManager::reflectClass($className);

            $reflectionProperty = $reflectionClass->getProperty($target);

            if (method_exists($reflectionProperty, 'hasType') && $reflectionProperty->hasType()) {
                /* @phpstan-ignore-next-line */
                $this->value = $reflectionProperty->getType()->getName();
            } else {
                $this->value = PhpDocReaderManager::getInstance()->getPropertyClass($reflectionProperty);
            }

            if (empty($this->value)) {
                throw new AnnotationException("The @Inject value is invalid for {$className}->{$target}");
            }

            if ($this->lazy) {
                $this->value = 'HyperfLazy\\' . $this->value;
            }
            AnnotationCollector::collectProperty($className, $target, static::class, $this);
        } catch (AnnotationException|DocReaderAnnotationException $exception) {
            if ($this->required) {
                throw new AnnotationException($exception->getMessage());
            }
            $this->value = '';
        } catch (\Throwable $exception) {
            throw new AnnotationException("The @Inject value is invalid for {$className}->{$target}. Because {$exception->getMessage()}");
        }
    }
}


#Hyperf\Di\Annotation\AbstractAnnotation
public function __construct(...$value)
    {
        $formattedValue = $this->formatParams($value);
        foreach ($formattedValue as $key => $val) {
            if (property_exists($this, $key)) {
                $this->{$key} = $val;
            }
        }
    }
    

 通过注释实现缓存,主要是通过注入一个自定义类,在自定义类中设置注释。

# App\Controller\TestController 自定义控制器
     /**
     * @Inject
     * @var Cache1Service
     */
     public function index1(RequestInterface $request)
    {
        $user = $request->input('user', 'Hyperf');
        $result = $this->service->getCache($user);
        return $this->response->json($result);
    }
#App\Service\Cache1Service
     public function get($user = 'test')
    {
        sleep(2);
        return $user . "_" . time();
    }
    /**
     * @Cacheable(prefix="cache",ttl=10, listener="user-update")
     */
    public function getCache($user = "test")
    {
        return $this->get($user);
    }

四、参考教程

b站视频:

【Hyperf 缓存】https://www.bilibili.com/video/BV1TS4y1g7tQ?vd_source=f1bd3b5218c30adf0a002c8c937e0a27

【Hyperf 2.0 - Inject 和 Aop】https://www.bilibili.com/video/BV1VT4y167cb?vd_source=f1bd3b5218c30adf0a002c8c937e0a27

强烈推荐,应该是swoole官方教程。

hyperf框架-设计模式(二)_hyperf inject_Aring88的博客-CSDN博客

百度结,但是果没看懂

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

lsswear

感谢大佬打赏 q(≧▽≦q)

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值