Compare commits

..

No commits in common. "master" and "old" have entirely different histories.
master ... old

3 changed files with 195 additions and 171 deletions

View File

@ -1,86 +0,0 @@
<?php
namespace dominion\cache;
use Yii;
use Predis\Client;
use yii\helpers\Inflector;
class Connection extends \yii\redis\Connection
{
/**
* @var mixed Connection parameters for one or more servers.
*/
public $parameters;
/**
* @var mixed Options to configure some behaviours of the client.
*/
public $options = [];
/**
* @var Client redis socket connection
*/
private $_socket = false;
public function __call($name, $params)
{
$redisCommand = strtoupper(Inflector::camel2words($name, false));
if (in_array($redisCommand, $this->redisCommands)) {
return $this->executeCommand($redisCommand, $params);
} else {
return parent::__call($name, $params);
}
}
public function executeCommand($name, $params = [])
{
$this->open();
Yii::debug("Executing Redis Command: {$name} " . implode(' ', $params), __METHOD__);
return $this->_socket->executeCommand(
$this->_socket->createCommand($name, $params)
);
}
/**
* Closes the connection when this component is being serialized.
* @return array
*/
public function __sleep()
{
$this->close();
return array_keys(get_object_vars($this));
}
/**
* Returns a value indicating whether the DB connection is established.
* @return bool whether the DB connection is established
*/
public function getIsActive()
{
return $this->_socket !== false;
}
public function open()
{
if ($this->_socket !== false) {
return;
}
Yii::debug('Opening redis DB connection: ' /*. var_export($this->parameters, true)*/, __METHOD__);
$this->_socket = new Client($this->parameters, $this->options);
$this->initConnection();
}
/**
* Closes the currently active DB connection.
* It does nothing if the connection is already closed.
*/
public function close()
{
if ($this->_socket !== false) {
Yii::debug('Closing DB connection: ' . var_export($this->parameters, true), __METHOD__);
$this->_socket->disconnect();
$this->_socket = false;
}
}
}

View File

@ -3,34 +3,35 @@
namespace dominion\cache; namespace dominion\cache;
use Yii; use Yii;
use Redis;
use yii\console\Exception;
class RedisCache class RedisCache
{ {
protected static $redis;
/** /**
* используется для отключения кеширования * используется для отключения кеширования
* например при реиндексе или дебаге * например при реиндексе или дебаге
* @var type * @var type
*/ */
protected static $active = true; protected static $active = true;
/** /**
* время жизни кеша в секундах * время жизни кеша в секундах
* @var int * @var int
*/ */
protected static $livetime; protected static $livetime = 300;
/** /**
* начальная директория для кеша в редисе * начальная директория для кеша в редисе
* @var type * @var type
*/ */
protected static $prefix; protected static $prefix = '';
/** /**
* Время чкрез ктр удалить кешь * Время чкрез ктр удалить кешь
* @var int * @var int
*/ */
protected static $keysDeleteTime = 30; protected static $keysDeleteTime = 30;
protected static $setDeleteKey = false; protected static $setDeleteKey = false;
public static function setActive($active) public static function setActive($active)
@ -48,56 +49,93 @@ class RedisCache
self::$setDeleteKey = $setDeleteKey; self::$setDeleteKey = $setDeleteKey;
} }
protected static function livetime() public static function connection($reConnect = false)
{ {
if (!self::$livetime) if(!self::$redis || $reConnect)
{ {
self::$livetime = isset(Yii::$app->params['redis'], Yii::$app->params['redis']['livetime']) ? (int) Yii::$app->params['redis']['livetime'] : 300; if(empty(Yii::$app->params['redis'])) {
throw new Exception('Отсутствует конфигурация для redis');
}
if(empty(Yii::$app->params['redis']['host'])) {
throw new Exception('Отсутствует конфигурация для redis host');
}
if(empty(Yii::$app->params['redis']['port'])) {
throw new Exception('Отсутствует конфигурация для redis port');
}
if(empty(Yii::$app->params['redis']['prefix'])) {
throw new Exception('Отсутствует конфигурация для redis prefix');
}
$redis = new Redis;
$redis->connect(Yii::$app->params['redis']['host'], Yii::$app->params['redis']['port']);
$redis->setOption(Redis::OPT_SERIALIZER, Redis::SERIALIZER_IGBINARY);
$redis->setOption(Redis::OPT_PREFIX, Yii::$app->params['redis']['prefix']);
$redis->setOption(Redis::OPT_SCAN, Redis::SCAN_RETRY);
self::$redis = $redis;
self::$livetime = isset(Yii::$app->params['redis']['livetime']) ? (int)Yii::$app->params['redis']['livetime'] : self::$livetime;
self::$prefix = Yii::$app->params['redis']['prefix'];
} }
return self::$livetime; return self::$redis;
} }
protected static function prefix() public static function hdel($key, $value, $connect)
{ {
if (!self::$prefix) if(self::$setDeleteKey)
{
self::$prefix = isset(Yii::$app->params['redis'], Yii::$app->params['redis']['prefix']) ? (string)Yii::$app->params['redis']['prefix'] : '';
}
return self::$prefix;
}
protected static function calculateKey($key)
{
return self::prefix() . $key;
}
public static function hdel($key, $value)
{
if (self::$setDeleteKey)
{ {
self::addDeleteKey($key, $value); self::addDeleteKey($key, $value);
} }
else else
{ {
Yii::$app->redis->hdel(self::calculateKey($key), $value); $prefix = self::$prefix;
$raw = "HDEL {$prefix}{$key} {$value}";
Yii::beginProfile($raw, 'yii\db\Command::query');
$connect->hdel($key, $value);
Yii::endProfile($raw, 'yii\db\Command::query');
} }
} }
public static function del($key, $realDelete = false) public static function del($key, $realDelete = false)
{ {
if (self::$setDeleteKey && !$realDelete) if(self::$setDeleteKey && !$realDelete)
{ {
self::addDeleteKey($key); self::addDeleteKey($key);
} }
else else
{ {
Yii::$app->redis->del(self::calculateKey($key)); $prefix = self::$prefix;
$raw = "DEL {$prefix}{$key}";
Yii::beginProfile($raw, 'yii\db\Command::query');
try
{
self::connection()->del($key);
}
catch (\Exception $exc)
{
self::connection(true)->del($key);
}
Yii::endProfile($raw, 'yii\db\Command::query');
} }
} }
public static function hset($key, $field, $value) public static function hset($key, $field, $value)
{ {
return self::getActive() ? Yii::$app->redis->hset(self::calculateKey($key), $field, json_encode($value)) : false; $result = false;
if(self::getActive())
{
$prefix = self::$prefix;
$raw = "HSET {$prefix}{$key} {$field}";
Yii::beginProfile($raw, 'yii\db\Command::query');
try
{
$result = self::connection()->hset($key, $field, $value);
}
catch (\Exception $exc)
{
$result = self::connection(true)->hset($key, $field, $value);
}
Yii::endProfile($raw, 'yii\db\Command::query');
}
return $result;
} }
public static function hsetModel($key, $field, $model) public static function hsetModel($key, $field, $model)
@ -120,7 +158,7 @@ class RedisCache
{ {
$values[] = $model->attributes; $values[] = $model->attributes;
} }
if ($page) if($page)
{ {
$values['page'] = [ $values['page'] = [
'totalCount' => $page->totalCount, 'totalCount' => $page->totalCount,
@ -134,12 +172,23 @@ class RedisCache
public static function hget($key, $field) public static function hget($key, $field)
{ {
$output = false; $result = false;
if(self::getActive()) if(self::getActive())
{ {
$output = json_decode(Yii::$app->redis->hget(self::calculateKey($key), $field), true); $prefix = self::$prefix;
$raw = "HGET {$prefix}{$key} {$field}";
Yii::beginProfile($raw, 'yii\db\Command::query');
try
{
$result = self::connection()->hget($key, $field);
}
catch (\Exception $exc)
{
$result = self::connection(true)->hget($key, $field);
}
Yii::endProfile($raw, 'yii\db\Command::query');
} }
return $output === null ? false : $output;; return $result;
} }
public static function hgetModel($key, $field, $className) public static function hgetModel($key, $field, $className)
@ -150,19 +199,19 @@ class RedisCache
public static function hgetModelArray($key, $field, $className, $page = null) public static function hgetModelArray($key, $field, $className, $page = null)
{ {
if ($page) if($page)
{ {
$field .= ":page:{$page->page}:perPage:{$page->perPage}:sort:{$page->sort}"; $field .= ":page:{$page->page}:perPage:{$page->perPage}:sort:{$page->sort}";
} }
$values = self::hget($key, $field); $values = self::hget($key, $field);
if ($values === false) if($values === false)
{ {
return false; return false;
} }
$ids = []; $ids = [];
foreach ($values as $keyVal => $value) foreach ($values as $keyVal => $value)
{ {
if ($keyVal !== 'page' && isset($value['id'])) if($keyVal !== 'page' && isset($value['id']))
{ {
$ids[] = $value['id']; $ids[] = $value['id'];
} }
@ -170,9 +219,9 @@ class RedisCache
$output = []; $output = [];
foreach ($values as $keyVal => $value) foreach ($values as $keyVal => $value)
{ {
if ($keyVal === 'page') if($keyVal === 'page')
{ {
if ($page) if($page)
{ {
$page->setMeta($value['totalCount'], $value['perPage']); $page->setMeta($value['totalCount'], $value['perPage']);
$page->sort = $value['sort']; $page->sort = $value['sort'];
@ -181,7 +230,7 @@ class RedisCache
else else
{ {
$model = new $className($value); $model = new $className($value);
if (isset($model->ids)) if(isset($model->ids))
{ {
$model->ids = $ids; $model->ids = $ids;
} }
@ -193,24 +242,29 @@ class RedisCache
public static function delete($key, array $patterns) public static function delete($key, array $patterns)
{ {
if (self::getActive()) if(self::getActive())
{ {
foreach ($patterns as $pattern) $prefix = self::$prefix;
$connect = self::connection();
foreach($patterns as $pattern)
{ {
$cursor = 0;//NULL; $cursor = NULL;
foreach ($patterns as $pattern) foreach ($patterns as $pattern)
{ {
$arKeys = []; $arKeys = [];
while ($values = Yii::$app->redis->hscan(self::calculateKey($key), $cursor, $pattern)) $raw = "HSCAN {$prefix}{$key} {$cursor} MATCH {$pattern}";
Yii::beginProfile($raw, 'yii\db\Command::query');
while($values = $connect->hscan($key, $cursor, $pattern))
{ {
foreach ($values as $vkey => $value) foreach($values as $vkey => $value)
{ {
$arKeys[] = $vkey; $arKeys[] = $vkey;
} }
} }
foreach ($arKeys as $value) Yii::endProfile($raw, 'yii\db\Command::query');
foreach($arKeys as $value)
{ {
self::hdel($key, $value); self::hdel($key, $value, $connect);
} }
} }
} }
@ -224,7 +278,7 @@ class RedisCache
*/ */
public static function deleteAll($key, $realDelete = false) public static function deleteAll($key, $realDelete = false)
{ {
if (self::getActive()) if(self::getActive())
{ {
self::del($key, $realDelete); self::del($key, $realDelete);
} }
@ -232,13 +286,23 @@ class RedisCache
public static function getKeyTypes() public static function getKeyTypes()
{ {
$prefix = self::$prefix;
$result = Yii::$app->redis->keys(self::calculateKey('*')); $raw = "KEYS {$prefix}*";
Yii::beginProfile($raw, 'yii\db\Command::query');
try
{
$result = self::connection()->keys('*');
}
catch (\Exception $exc)
{
$result = self::connection(true)->keys('*');
}
Yii::endProfile($raw, 'yii\db\Command::query');
$output = ['*']; $output = ['*'];
foreach ($result as $val) foreach ($result as $val)
{ {
$arVal = explode(':', $val); $arVal = explode(':', $val);
if (isset($arVal[1]) && !in_array($arVal[1], $output)) if(isset($arVal[1]) && !in_array($arVal[1], $output))
{ {
$output[] = $arVal[1]; $output[] = $arVal[1];
} }
@ -254,14 +318,25 @@ class RedisCache
*/ */
public static function deleteType($type) public static function deleteType($type)
{ {
if (self::getActive()) if(self::getActive())
{ {
self::deleteAll($type); self::deleteAll($type);
$type = $type == "*" ? "*" : "{$type}:*"; $type = $type == "*" ? "*" : "{$type}:*";
$result = Yii::$app->redis->keys(self::calculateKey($type)); $prefix = self::$prefix;
$raw = "KEYS {$prefix}{$type}";
Yii::beginProfile($raw, 'yii\db\Command::query');
try
{
$result = self::connection()->keys($type);
}
catch (\Exception $exc)
{
$result = self::connection(true)->keys($type);
}
Yii::endProfile($raw, 'yii\db\Command::query');
foreach ($result as $val) foreach ($result as $val)
{ {
self::deleteAll(str_replace(self::prefix(), '', $val)); self::deleteAll(str_replace($prefix, '', $val));
} }
} }
return true; return true;
@ -270,17 +345,34 @@ class RedisCache
public static function set($key, $value, $options = null) public static function set($key, $value, $options = null)
{ {
$result = false; $result = false;
if (self::getActive()) if(self::getActive())
{ {
if ($options === true) if($options === true)
{ {
$options = ['EX' => self::livetime()]; $options = ['EX' => self::$livetime];
} }
elseif (is_int($options)) elseif(is_int($options))
{ {
$options = ['EX' => $options]; $options = ['EX' => $options];
} }
Yii::$app->redis->set(self::calculateKey($key), json_encode($value) .' '. implode(' ', $options)); $addStr = '';
foreach((array)$options as $okey => $oval)
{
$addStr .= "{$okey} {$oval}";
}
$prefix = self::$prefix;
$json = json_encode($value);
$raw = "SET {$prefix}{$key} {$json} {$addStr}";
Yii::beginProfile($raw, 'yii\db\Command::query');
try
{
$result = self::connection()->set($key, $json, $options);
}
catch (\Exception $exc)
{
$result = self::connection(true)->set($key, $json, $options);
}
Yii::endProfile($raw, 'yii\db\Command::query');
} }
return $result; return $result;
} }
@ -297,7 +389,7 @@ class RedisCache
{ {
$values[] = $model->attributes; $values[] = $model->attributes;
} }
if ($page) if($page)
{ {
$values['page'] = [ $values['page'] = [
'totalCount' => $page->totalCount, 'totalCount' => $page->totalCount,
@ -311,11 +403,22 @@ class RedisCache
public static function get(string $key) public static function get(string $key)
{ {
$result = false; $result = false;
if (self::getActive()) if(self::getActive())
{ {
$result = json_decode(Yii::$app->redis->get(self::calculateKey($key)), true); $prefix = self::$prefix;
$raw = "GET {$prefix}{$key}";
Yii::beginProfile($raw, 'yii\db\Command::query');
try
{
$result = self::connection()->get($key);
}
catch (\Exception $exc)
{
$result = self::connection(true)->get($key);
}
Yii::endProfile($raw, 'yii\db\Command::query');
} }
return $result === null ? false : $result; return $result ? json_decode($result, true) : $result;
} }
public static function getModel($key, $className) public static function getModel($key, $className)
@ -327,14 +430,14 @@ class RedisCache
public static function getModelArray($key, $className, $page = null) public static function getModelArray($key, $className, $page = null)
{ {
$values = self::get($key); $values = self::get($key);
if ($values === false) if($values === false)
{ {
return false; return false;
} }
$ids = []; $ids = [];
foreach ($values as $keyVal => $value) foreach ($values as $keyVal => $value)
{ {
if ($keyVal !== 'page' && isset($value['id'])) if($keyVal !== 'page' && isset($value['id']))
{ {
$ids[] = $value['id']; $ids[] = $value['id'];
} }
@ -342,9 +445,9 @@ class RedisCache
$output = []; $output = [];
foreach ($values as $keyVal => $value) foreach ($values as $keyVal => $value)
{ {
if ($keyVal === 'page') if($keyVal === 'page')
{ {
if ($page) if($page)
{ {
$page->setMeta($value['totalCount'], $value['perPage']); $page->setMeta($value['totalCount'], $value['perPage']);
$page->sort = $value['sort']; $page->sort = $value['sort'];
@ -353,7 +456,7 @@ class RedisCache
else else
{ {
$model = new $className($value); $model = new $className($value);
if (isset($model->ids)) if(isset($model->ids))
{ {
$model->ids = $ids; $model->ids = $ids;
} }
@ -371,46 +474,55 @@ class RedisCache
*/ */
public static function addDeleteKey($key, $field = '-') public static function addDeleteKey($key, $field = '-')
{ {
return self::hset('del:' . $key, $field, time() + self::$keysDeleteTime); return self::hset('del:'.$key, $field, time() + self::$keysDeleteTime);
} }
/** /**
* Удаляет устаревший кеш * Удаляет устаревший кеш
* @return type * @return type
*/ */
public static function oldCacheDelete() public static function oldCacheDelete()
{ {
$result = Yii::$app->redis->keys(self::calculateKey('del:*')); $connect = self::connection();
$prefix = self::$prefix;
$raw = "KEYS {$prefix}del:*";
Yii::beginProfile($raw, 'yii\db\Command::query');
$result = $connect->keys('del:*');
Yii::endProfile($raw, 'yii\db\Command::query');
foreach ($result as $val) foreach ($result as $val)
{ {
$cursor = 0;//null; $cursor = null;
$key = str_replace(self::prefix(), '', $val); //$key = str_replace("{$prefix}del:", '', $val);
$key = str_replace($prefix, '', $val);
$pattern = '*'; $pattern = '*';
$arKeys = []; $arKeys = [];
while ($values = Yii::$app->redis->hscan(self::calculateKey($key), $cursor, $pattern)) $raw = "HSCAN {$prefix}{$key} {$cursor} MATCH {$pattern}";
Yii::beginProfile($raw, 'yii\db\Command::query');
while($values = $connect->hscan($key, $cursor, $pattern))
{ {
foreach ($values as $vkey => $value) foreach($values as $vkey => $value)
{ {
if ($value < time()) if($value < time())
{ {
$arKeys[] = $vkey; $arKeys[] = $vkey;
} }
} }
} }
$keyCache = str_replace(self::prefix() . "del:", '', $val); $keyCache = str_replace("{$prefix}del:", '', $val);
foreach ($arKeys as $value) Yii::endProfile($raw, 'yii\db\Command::query');
foreach($arKeys as $value)
{ {
self::$setDeleteKey = false; self::$setDeleteKey = false;
if ($value == '-') if($value == '-')
{ {
self::del($keyCache); self::del($keyCache, $connect);
} }
else else
{ {
self::hdel($keyCache, $value); self::hdel($keyCache, $value, $connect);
} }
self::hdel($key, $value); self::hdel($key, $value, $connect);
} }
} }
} }
} }

View File

@ -11,9 +11,7 @@
} }
], ],
"require": { "require": {
"yiisoft/yii2-redis": "~2.0.0", "yiisoft/yii2": "~2.0.0"
"yiisoft/yii2": "~2.0.0",
"predis/predis": "^3.0.1"
}, },
"autoload": { "autoload": {
"psr-4": { "psr-4": {