From 5d359453e6a579991e9eb787a063d3a77ac3d87a Mon Sep 17 00:00:00 2001 From: Sasha Rybkin Date: Mon, 14 Apr 2025 15:11:15 +0300 Subject: [PATCH] =?UTF-8?q?#110864=20=D0=9A=D0=BE=D0=BD=D1=84=D0=B8=D0=B3?= =?UTF-8?q?=D1=83=D1=80=D0=B0=D1=86=D0=B8=D1=8F=20yii=20=D0=B4=D0=BB=D1=8F?= =?UTF-8?q?=20=D1=80=D0=B0=D0=B1=D0=BE=D1=82=D1=8B=20=D1=81=20=D0=BA=D0=BB?= =?UTF-8?q?=D0=B0=D1=81=D1=82=D0=B5=D1=80=D0=BD=D1=8B=D0=BC=20redis?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Connection.php | 85 ++++++++++++++++ RedisCache.php | 270 ++++++++++++++----------------------------------- 2 files changed, 162 insertions(+), 193 deletions(-) create mode 100644 Connection.php diff --git a/Connection.php b/Connection.php new file mode 100644 index 0000000..1778e22 --- /dev/null +++ b/Connection.php @@ -0,0 +1,85 @@ +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; + } + } + +} diff --git a/RedisCache.php b/RedisCache.php index 7cb8a17..38cd531 100644 --- a/RedisCache.php +++ b/RedisCache.php @@ -3,35 +3,34 @@ namespace dominion\cache; use Yii; -use Redis; -use yii\console\Exception; class RedisCache { - protected static $redis; + /** * используется для отключения кеширования * например при реиндексе или дебаге * @var type */ protected static $active = true; + /** * время жизни кеша в секундах * @var int */ - protected static $livetime = 300; + protected static $livetime; + /** * начальная директория для кеша в редисе * @var type */ - protected static $prefix = ''; + protected static $prefix; /** * Время чкрез ктр удалить кешь * @var int */ protected static $keysDeleteTime = 30; - protected static $setDeleteKey = false; public static function setActive($active) @@ -49,93 +48,56 @@ class RedisCache self::$setDeleteKey = $setDeleteKey; } - public static function connection($reConnect = false) + protected static function livetime() { - if(!self::$redis || $reConnect) + if (!self::$livetime) { - 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']; + self::$livetime = isset(Yii::$app->params['redis'], Yii::$app->params['redis']['livetime']) ? (int) Yii::$app->params['redis']['livetime'] : 300; } - return self::$redis; + return self::$livetime; } - public static function hdel($key, $value, $connect) + protected static function prefix() { - if(self::$setDeleteKey) + if (!self::$prefix) + { + self::$prefix = isset(Yii::$app->params['redis'], Yii::$app->params['redis']['prefix']) ? (int) 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); } else { - $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'); + Yii::$app->redis->hdel(self::calculateKey($key), $value); } } + public static function del($key, $realDelete = false) { - if(self::$setDeleteKey && !$realDelete) + if (self::$setDeleteKey && !$realDelete) { self::addDeleteKey($key); } else { - $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'); + Yii::$app->redis->del(self::calculateKey($key)); } } public static function hset($key, $field, $value) { - $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; + return self::getActive() ? Yii::$app->redis->hset(self::calculateKey($key), $field, $value) : false; } public static function hsetModel($key, $field, $model) @@ -158,7 +120,7 @@ class RedisCache { $values[] = $model->attributes; } - if($page) + if ($page) { $values['page'] = [ 'totalCount' => $page->totalCount, @@ -172,23 +134,7 @@ class RedisCache public static function hget($key, $field) { - $result = false; - if(self::getActive()) - { - $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 $result; + return self::getActive() ? Yii::$app->redis->hget(self::calculateKey($key), $field) : false; } public static function hgetModel($key, $field, $className) @@ -199,19 +145,19 @@ class RedisCache public static function hgetModelArray($key, $field, $className, $page = null) { - if($page) + if ($page) { $field .= ":page:{$page->page}:perPage:{$page->perPage}:sort:{$page->sort}"; } $values = self::hget($key, $field); - if($values === false) + if ($values === false) { return false; } $ids = []; foreach ($values as $keyVal => $value) { - if($keyVal !== 'page' && isset($value['id'])) + if ($keyVal !== 'page' && isset($value['id'])) { $ids[] = $value['id']; } @@ -219,9 +165,9 @@ class RedisCache $output = []; foreach ($values as $keyVal => $value) { - if($keyVal === 'page') + if ($keyVal === 'page') { - if($page) + if ($page) { $page->setMeta($value['totalCount'], $value['perPage']); $page->sort = $value['sort']; @@ -230,7 +176,7 @@ class RedisCache else { $model = new $className($value); - if(isset($model->ids)) + if (isset($model->ids)) { $model->ids = $ids; } @@ -242,29 +188,24 @@ class RedisCache public static function delete($key, array $patterns) { - if(self::getActive()) + if (self::getActive()) { - $prefix = self::$prefix; - $connect = self::connection(); - foreach($patterns as $pattern) + foreach ($patterns as $pattern) { $cursor = NULL; foreach ($patterns as $pattern) { $arKeys = []; - $raw = "HSCAN {$prefix}{$key} {$cursor} MATCH {$pattern}"; - Yii::beginProfile($raw, 'yii\db\Command::query'); - while($values = $connect->hscan($key, $cursor, $pattern)) + while ($values = Yii::$app->redis->hscan(self::calculateKey($key), $cursor, $pattern)) { - foreach($values as $vkey => $value) + foreach ($values as $vkey => $value) { $arKeys[] = $vkey; } } - Yii::endProfile($raw, 'yii\db\Command::query'); - foreach($arKeys as $value) + foreach ($arKeys as $value) { - self::hdel($key, $value, $connect); + self::hdel($key, $value); } } } @@ -278,7 +219,7 @@ class RedisCache */ public static function deleteAll($key, $realDelete = false) { - if(self::getActive()) + if (self::getActive()) { self::del($key, $realDelete); } @@ -286,23 +227,13 @@ class RedisCache public static function getKeyTypes() { - $prefix = self::$prefix; - $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'); + + $result = Yii::$app->redis->keys(self::calculateKey('*')); $output = ['*']; foreach ($result as $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]; } @@ -318,25 +249,14 @@ class RedisCache */ public static function deleteType($type) { - if(self::getActive()) + if (self::getActive()) { self::deleteAll($type); $type = $type == "*" ? "*" : "{$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'); + $result = Yii::$app->redis->keys(self::calculateKey($type)); foreach ($result as $val) { - self::deleteAll(str_replace($prefix, '', $val)); + self::deleteAll(str_replace(self::prefix(), '', $val)); } } return true; @@ -345,34 +265,18 @@ class RedisCache public static function set($key, $value, $options = null) { $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]; } - $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'); + Yii::$app->redis->set(self::calculateKey($key), $json, $options); } return $result; } @@ -389,7 +293,7 @@ class RedisCache { $values[] = $model->attributes; } - if($page) + if ($page) { $values['page'] = [ 'totalCount' => $page->totalCount, @@ -403,20 +307,9 @@ class RedisCache public static function get(string $key) { $result = false; - if(self::getActive()) + if (self::getActive()) { - $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'); + $result = Yii::$app->redis->get(self::calculateKey($key)); } return $result ? json_decode($result, true) : $result; } @@ -430,14 +323,14 @@ class RedisCache public static function getModelArray($key, $className, $page = null) { $values = self::get($key); - if($values === false) + if ($values === false) { return false; } $ids = []; foreach ($values as $keyVal => $value) { - if($keyVal !== 'page' && isset($value['id'])) + if ($keyVal !== 'page' && isset($value['id'])) { $ids[] = $value['id']; } @@ -445,9 +338,9 @@ class RedisCache $output = []; foreach ($values as $keyVal => $value) { - if($keyVal === 'page') + if ($keyVal === 'page') { - if($page) + if ($page) { $page->setMeta($value['totalCount'], $value['perPage']); $page->sort = $value['sort']; @@ -456,7 +349,7 @@ class RedisCache else { $model = new $className($value); - if(isset($model->ids)) + if (isset($model->ids)) { $model->ids = $ids; } @@ -474,55 +367,46 @@ class RedisCache */ 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 */ public static function oldCacheDelete() { - $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'); + $result = Yii::$app->redis->keys(self::calculateKey('del:*')); foreach ($result as $val) { $cursor = null; - //$key = str_replace("{$prefix}del:", '', $val); - $key = str_replace($prefix, '', $val); + $key = str_replace(self::prefix(), '', $val); $pattern = '*'; $arKeys = []; - $raw = "HSCAN {$prefix}{$key} {$cursor} MATCH {$pattern}"; - Yii::beginProfile($raw, 'yii\db\Command::query'); - while($values = $connect->hscan($key, $cursor, $pattern)) + while ($values = Yii::$app->redis->hscan(self::calculateKey($key), $cursor, $pattern)) { - foreach($values as $vkey => $value) + foreach ($values as $vkey => $value) { - if($value < time()) + if ($value < time()) { $arKeys[] = $vkey; } } } - $keyCache = str_replace("{$prefix}del:", '', $val); - Yii::endProfile($raw, 'yii\db\Command::query'); - foreach($arKeys as $value) + $keyCache = str_replace(self::prefix() . "del:", '', $val); + foreach ($arKeys as $value) { self::$setDeleteKey = false; - if($value == '-') + if ($value == '-') { - self::del($keyCache, $connect); + self::del($keyCache); } else { - self::hdel($keyCache, $value, $connect); + self::hdel($keyCache, $value); } - self::hdel($key, $value, $connect); + self::hdel($key, $value); } } } - }