mirror of
https://gitee.com/hyperf/hyperf.git
synced 2024-11-29 18:27:44 +08:00
Added Model Cacheable.
This commit is contained in:
parent
3caba80d1e
commit
194b66faa7
@ -16,6 +16,7 @@
|
||||
"psr/http-message": "^1.0.1",
|
||||
"psr/http-server-middleware": "^1.0",
|
||||
"psr/event-dispatcher": "^0.7",
|
||||
"psr/simple-cache": "^1.0",
|
||||
"fig/http-message-util": "^1.1.2",
|
||||
"nikic/fast-route": "^1.3",
|
||||
"nikic/php-parser": "^4.1",
|
||||
|
@ -12,6 +12,7 @@
|
||||
},
|
||||
"require": {
|
||||
"php": ">=7.2",
|
||||
"psr/simple-cache": "^1.0",
|
||||
"hyperf/database": "dev-master",
|
||||
"hyperf/di": "dev-master",
|
||||
"hyperf/event": "dev-master",
|
||||
|
30
src/db-connection/src/Cache/Cacheable.php
Normal file
30
src/db-connection/src/Cache/Cacheable.php
Normal file
@ -0,0 +1,30 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
/**
|
||||
* This file is part of Hyperf.
|
||||
*
|
||||
* @link https://hyperf.org
|
||||
* @document https://wiki.hyperf.org
|
||||
* @contact group@hyperf.org
|
||||
* @license https://github.com/hyperf-cloud/hyperf/blob/master/LICENSE
|
||||
*/
|
||||
|
||||
namespace Hyperf\DbConnection\Cache;
|
||||
|
||||
use Hyperf\Framework\ApplicationContext;
|
||||
|
||||
trait Cacheable
|
||||
{
|
||||
public static function findFromCache($id)
|
||||
{
|
||||
$container = ApplicationContext::getContainer();
|
||||
$manager = $container->get(Manager::class);
|
||||
|
||||
return $manager->findFromCache($id, static::class);
|
||||
}
|
||||
|
||||
public static function findManyFromCache($ids)
|
||||
{
|
||||
}
|
||||
}
|
102
src/db-connection/src/Cache/Config.php
Normal file
102
src/db-connection/src/Cache/Config.php
Normal file
@ -0,0 +1,102 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
/**
|
||||
* This file is part of Hyperf.
|
||||
*
|
||||
* @link https://hyperf.org
|
||||
* @document https://wiki.hyperf.org
|
||||
* @contact group@hyperf.org
|
||||
* @license https://github.com/hyperf-cloud/hyperf/blob/master/LICENSE
|
||||
*/
|
||||
|
||||
namespace Hyperf\DbConnection\Cache;
|
||||
|
||||
class Config
|
||||
{
|
||||
/**
|
||||
* Model cache key.
|
||||
*
|
||||
* mc:$prefix:m:$model:$pk:$id
|
||||
* You can rewrite it in Redis cluster, for examqple {mc:$prefix:m:$model}:$pk:$id
|
||||
* @var string
|
||||
*/
|
||||
protected $cacheKey = 'mc:%s:m:%s:%s:%s';
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $prefix = 'hyperf';
|
||||
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
protected $ttl = 3600;
|
||||
|
||||
/**
|
||||
* @var bool
|
||||
*/
|
||||
protected $loadScript = true;
|
||||
|
||||
public function __construct(array $values, string $name)
|
||||
{
|
||||
if (isset($values['cache_key'])) {
|
||||
$this->cacheKey = $values['cache_key'];
|
||||
}
|
||||
if (isset($values['prefix'])) {
|
||||
$this->prefix = $values['prefix'];
|
||||
} else {
|
||||
$this->prefix = $name;
|
||||
}
|
||||
if (isset($values['ttl'])) {
|
||||
$this->ttl = $values['ttl'];
|
||||
}
|
||||
if (isset($values['load_script'])) {
|
||||
$this->loadScript = $values['load_script'];
|
||||
}
|
||||
}
|
||||
|
||||
public function getCacheKey(): string
|
||||
{
|
||||
return $this->cacheKey;
|
||||
}
|
||||
|
||||
public function setCacheKey(string $cacheKey): Config
|
||||
{
|
||||
$this->cacheKey = $cacheKey;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getPrefix(): string
|
||||
{
|
||||
return $this->prefix;
|
||||
}
|
||||
|
||||
public function setPrefix(string $prefix): Config
|
||||
{
|
||||
$this->prefix = $prefix;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getTtl(): int
|
||||
{
|
||||
return $this->ttl;
|
||||
}
|
||||
|
||||
public function setTtl(int $ttl): Config
|
||||
{
|
||||
$this->ttl = $ttl;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function isLoadScript(): bool
|
||||
{
|
||||
return $this->loadScript;
|
||||
}
|
||||
|
||||
public function setLoadScript(bool $loadScript): Config
|
||||
{
|
||||
$this->loadScript = $loadScript;
|
||||
return $this;
|
||||
}
|
||||
}
|
17
src/db-connection/src/Cache/Exception/CacheException.php
Normal file
17
src/db-connection/src/Cache/Exception/CacheException.php
Normal file
@ -0,0 +1,17 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
/**
|
||||
* This file is part of Hyperf.
|
||||
*
|
||||
* @link https://hyperf.org
|
||||
* @document https://wiki.hyperf.org
|
||||
* @contact group@hyperf.org
|
||||
* @license https://github.com/hyperf-cloud/hyperf/blob/master/LICENSE
|
||||
*/
|
||||
|
||||
namespace Hyperf\DbConnection\Cache\Exception;
|
||||
|
||||
class CacheException extends \RuntimeException
|
||||
{
|
||||
}
|
21
src/db-connection/src/Cache/Handler/HandlerInterface.php
Normal file
21
src/db-connection/src/Cache/Handler/HandlerInterface.php
Normal file
@ -0,0 +1,21 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
/**
|
||||
* This file is part of Hyperf.
|
||||
*
|
||||
* @link https://hyperf.org
|
||||
* @document https://wiki.hyperf.org
|
||||
* @contact group@hyperf.org
|
||||
* @license https://github.com/hyperf-cloud/hyperf/blob/master/LICENSE
|
||||
*/
|
||||
|
||||
namespace Hyperf\DbConnection\Cache\Handler;
|
||||
|
||||
use Hyperf\DbConnection\Cache\Config;
|
||||
use Psr\SimpleCache\CacheInterface;
|
||||
|
||||
interface HandlerInterface extends CacheInterface
|
||||
{
|
||||
public function getConfig(): Config;
|
||||
}
|
149
src/db-connection/src/Cache/Handler/RedisHandler.php
Normal file
149
src/db-connection/src/Cache/Handler/RedisHandler.php
Normal file
@ -0,0 +1,149 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
/**
|
||||
* This file is part of Hyperf.
|
||||
*
|
||||
* @link https://hyperf.org
|
||||
* @document https://wiki.hyperf.org
|
||||
* @contact group@hyperf.org
|
||||
* @license https://github.com/hyperf-cloud/hyperf/blob/master/LICENSE
|
||||
*/
|
||||
|
||||
namespace Hyperf\DbConnection\Cache\Handler;
|
||||
|
||||
use Hyperf\DbConnection\Cache\Config;
|
||||
use Hyperf\DbConnection\Cache\Exception\CacheException;
|
||||
use Hyperf\DbConnection\Cache\Redis\HashsGetMultiple;
|
||||
use Hyperf\Utils\Contracts\Arrayable;
|
||||
use Psr\Container\ContainerInterface;
|
||||
use Redis;
|
||||
|
||||
class RedisHandler implements HandlerInterface
|
||||
{
|
||||
/**
|
||||
* @var ContainerInterface
|
||||
*/
|
||||
protected $container;
|
||||
|
||||
/**
|
||||
* @var Redis
|
||||
*/
|
||||
protected $redis;
|
||||
|
||||
/**
|
||||
* @var Config
|
||||
*/
|
||||
protected $config;
|
||||
|
||||
/**
|
||||
* @var HashsGetMultiple
|
||||
*/
|
||||
protected $multiple;
|
||||
|
||||
protected $luaSha = '';
|
||||
|
||||
protected $defaultHash = ['HF-DATA' => 'DEFAULT'];
|
||||
|
||||
public function __construct(ContainerInterface $container, Config $config)
|
||||
{
|
||||
$this->container = $container;
|
||||
if (! $container->has(Redis::class)) {
|
||||
throw new CacheException(sprintf('Entry[%s] of the container is not exist.', Redis::class));
|
||||
}
|
||||
|
||||
$this->redis = $container->get(Redis::class);
|
||||
$this->config = $config;
|
||||
$this->multiple = new HashsGetMultiple();
|
||||
}
|
||||
|
||||
public function get($key, $default = null)
|
||||
{
|
||||
$data = $this->redis->hGetAll($key);
|
||||
if (! $data) {
|
||||
return $default;
|
||||
}
|
||||
|
||||
if ($data == $this->defaultHash) {
|
||||
return $default;
|
||||
}
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
public function set($key, $value, $ttl = null)
|
||||
{
|
||||
if (is_array($value)) {
|
||||
$data = $value;
|
||||
} elseif ($value instanceof Arrayable) {
|
||||
$data = $value->toArray();
|
||||
} else {
|
||||
throw new CacheException(sprintf('The value must is array.'));
|
||||
}
|
||||
|
||||
$data = array_merge($data, $this->defaultHash);
|
||||
$res = $this->redis->hMSet($key, $data);
|
||||
if ($ttl && $ttl > 0) {
|
||||
$this->redis->expire($key, $ttl);
|
||||
}
|
||||
|
||||
return $res;
|
||||
}
|
||||
|
||||
public function delete($key)
|
||||
{
|
||||
return $this->redis->delete($key);
|
||||
}
|
||||
|
||||
public function clear()
|
||||
{
|
||||
throw new CacheException('Method clear is forbidden.');
|
||||
}
|
||||
|
||||
public function getMultiple($keys, $default = null)
|
||||
{
|
||||
if ($this->config->isLoadScript()) {
|
||||
$sha = $this->getLuaSha();
|
||||
}
|
||||
|
||||
if (! empty($sha)) {
|
||||
$list = $this->redis->evalSha($sha, $keys, count($keys));
|
||||
} else {
|
||||
$script = $this->multiple->getScript();
|
||||
$list = $this->redis->eval($script, $keys, count($keys));
|
||||
}
|
||||
|
||||
return $list;
|
||||
}
|
||||
|
||||
public function setMultiple($values, $ttl = null)
|
||||
{
|
||||
// TODO: Implement setMultiple() method.
|
||||
}
|
||||
|
||||
public function deleteMultiple($keys)
|
||||
{
|
||||
// TODO: Implement deleteMultiple() method.
|
||||
}
|
||||
|
||||
public function has($key)
|
||||
{
|
||||
// TODO: Implement has() method.
|
||||
}
|
||||
|
||||
public function getConfig(): Config
|
||||
{
|
||||
return $this->config;
|
||||
}
|
||||
|
||||
protected function getLuaSha()
|
||||
{
|
||||
if (! empty($this->luaSha)) {
|
||||
return $this->luaSha;
|
||||
}
|
||||
|
||||
$sha = $this->redis->script('load', $this->multiple->getScript());
|
||||
|
||||
return $this->luaSha = $sha;
|
||||
}
|
||||
}
|
102
src/db-connection/src/Cache/Manager.php
Normal file
102
src/db-connection/src/Cache/Manager.php
Normal file
@ -0,0 +1,102 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
/**
|
||||
* This file is part of Hyperf.
|
||||
*
|
||||
* @link https://hyperf.org
|
||||
* @document https://wiki.hyperf.org
|
||||
* @contact group@hyperf.org
|
||||
* @license https://github.com/hyperf-cloud/hyperf/blob/master/LICENSE
|
||||
*/
|
||||
|
||||
namespace Hyperf\DbConnection\Cache;
|
||||
|
||||
use Hyperf\Contract\ConfigInterface;
|
||||
use Hyperf\DbConnection\Cache\Handler\HandlerInterface;
|
||||
use Hyperf\DbConnection\Cache\Handler\RedisHandler;
|
||||
use Hyperf\DbConnection\Model\Model;
|
||||
use Hyperf\Framework\Contract\StdoutLoggerInterface;
|
||||
use Psr\Container\ContainerInterface;
|
||||
|
||||
class Manager
|
||||
{
|
||||
/**
|
||||
* @var ContainerInterface
|
||||
*/
|
||||
protected $container;
|
||||
|
||||
/**
|
||||
* @var HandlerInterface[]
|
||||
*/
|
||||
protected $handlers = [];
|
||||
|
||||
/**
|
||||
* @var StdoutLoggerInterface
|
||||
*/
|
||||
protected $logger;
|
||||
|
||||
public function __construct(ContainerInterface $container)
|
||||
{
|
||||
$this->container = $container;
|
||||
$this->logger = $container->get(StdoutLoggerInterface::class);
|
||||
$config = $container->get(ConfigInterface::class);
|
||||
if (! $config->has('databases')) {
|
||||
throw new \InvalidArgumentException('config databases is not exist!');
|
||||
}
|
||||
|
||||
foreach ($config->get('databases') as $key => $item) {
|
||||
$handler = $item['handler'] ?? RedisHandler::class;
|
||||
$config = new Config($item['cache'] ?? [], $key);
|
||||
$this->handlers[$key] = new $handler($this->container, $config);
|
||||
}
|
||||
}
|
||||
|
||||
public function findFromCache($id, string $class)
|
||||
{
|
||||
/** @var Model $instance */
|
||||
$instance = new $class();
|
||||
|
||||
$name = $instance->getConnectionName();
|
||||
$primaryKey = $instance->getKeyName();
|
||||
|
||||
if ($handler = $this->handlers[$name] ?? null) {
|
||||
$key = $this->getCacheKey($id, $instance, $handler->getConfig());
|
||||
$data = $handler->get($key);
|
||||
if ($data) {
|
||||
return $instance->newFromBuilder($data);
|
||||
}
|
||||
|
||||
if (is_null($data)) {
|
||||
$model = $instance->newQuery()->where($primaryKey, '=', $id)->first();
|
||||
if ($model) {
|
||||
$handler->set($key, $model->toArray());
|
||||
} else {
|
||||
$handler->set($key, []);
|
||||
}
|
||||
return $model;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
$this->logger->warning('Cache handler not exist, fetch data from database.');
|
||||
return $instance->newQuery()->where($primaryKey, '=', $id)->first();
|
||||
}
|
||||
|
||||
public function findManyFromCache(array $ids, string $class)
|
||||
{
|
||||
}
|
||||
|
||||
protected function getCacheKey($id, Model $model, Config $config)
|
||||
{
|
||||
// mc:$prefix:m:$model:$pk:$id
|
||||
return sprintf(
|
||||
$config->getCacheKey(),
|
||||
$config->getPrefix(),
|
||||
$model->getTable(),
|
||||
$model->getKeyName(),
|
||||
$id
|
||||
);
|
||||
}
|
||||
}
|
49
src/db-connection/src/Cache/Redis/HashsGetMultiple.php
Normal file
49
src/db-connection/src/Cache/Redis/HashsGetMultiple.php
Normal file
@ -0,0 +1,49 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
/**
|
||||
* This file is part of Hyperf.
|
||||
*
|
||||
* @link https://hyperf.org
|
||||
* @document https://wiki.hyperf.org
|
||||
* @contact group@hyperf.org
|
||||
* @license https://github.com/hyperf-cloud/hyperf/blob/master/LICENSE
|
||||
*/
|
||||
|
||||
namespace Hyperf\DbConnection\Cache\Redis;
|
||||
|
||||
class HashsGetMultiple implements OperatorInterface
|
||||
{
|
||||
public function getScript(): string
|
||||
{
|
||||
$command = <<<LUA
|
||||
local values = {};
|
||||
for i,v in ipairs(KEYS) do
|
||||
if(redis.call('type',v).ok == 'hash') then
|
||||
values[#values+1] = redis.call('hgetall',v);
|
||||
end
|
||||
end
|
||||
return values;
|
||||
LUA;
|
||||
|
||||
return $command;
|
||||
}
|
||||
|
||||
public function parseResponse($data)
|
||||
{
|
||||
$result = [];
|
||||
foreach ($data ?? [] as $item) {
|
||||
if (! empty($item) && is_array($item)) {
|
||||
$temp = [];
|
||||
$count = count($item);
|
||||
for ($i = 0; $i < $count; ++$i) {
|
||||
$temp[$item[$i]] = $item[++$i];
|
||||
}
|
||||
|
||||
$result[] = $temp;
|
||||
}
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
}
|
20
src/db-connection/src/Cache/Redis/OperatorInterface.php
Normal file
20
src/db-connection/src/Cache/Redis/OperatorInterface.php
Normal file
@ -0,0 +1,20 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
/**
|
||||
* This file is part of Hyperf.
|
||||
*
|
||||
* @link https://hyperf.org
|
||||
* @document https://wiki.hyperf.org
|
||||
* @contact group@hyperf.org
|
||||
* @license https://github.com/hyperf-cloud/hyperf/blob/master/LICENSE
|
||||
*/
|
||||
|
||||
namespace Hyperf\DbConnection\Cache\Redis;
|
||||
|
||||
interface OperatorInterface
|
||||
{
|
||||
public function getScript(): string;
|
||||
|
||||
public function parseResponse($data);
|
||||
}
|
@ -43,7 +43,7 @@ class Model extends BaseModel
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws RuntimeException When the model does not define the repository class.
|
||||
* @throws RuntimeException when the model does not define the repository class
|
||||
*/
|
||||
public function getRepository()
|
||||
{
|
||||
|
Loading…
Reference in New Issue
Block a user