场景:项目中到处可见的key,没有统一管理,极其难维护。大佬同事实现了一个。
代码
如图,Redis.php 是对redis的二次封装,对redis key模块的强制校验,FillerKeyTrait.php 是对filler模块的key获取。主要原理是:对redis二次封装,统一调用入口,对key的获取通过 方法获取,对key根据配置的MODULES进行校验。
一.对redis二次封装
Redis.php代码
<?php
namespace common\cache;
use common\cache\keys\FillerKeyTrait;
use common\tools\Dingtalk;
use yii\helpers\Inflector;
use yii\redis\Connection;
class Redis extends Connection
{
const YEAR = 60*60*24*365;
const DAY = 60*60*24;
const MONTH = 60*60*24*31;
use FillerKeyTrait;
const checkCommands = [
'set',
'setex',
'setnx',
'getset',
'hmset',
'hset',
'hsetnx',
'lset',
'psetex',
'setbit',
'incr',
'incrby',
'zincrby',
'incrbyfloat',
'hincrby',
'hincrbyfloat',
'decr',
'decrby',
'lpush',
'lpushx',
'rpush',
'rpushx',
'lset',
'linsert',
'sadd',
'zadd',
];
//均已空数组结尾
const MODULES = [
'filler' => [
],
];
public function __call($name, $params)
{
$redisCommand = strtoupper(Inflector::camel2words($name, false));
if (in_array($redisCommand, self::checkCommands)) {
if (!$this->checkKey($params[0])) {
return;
}
}
return parent::__call($name, $params);
}
public function get($key)
{
$value = parent::get($key);
$result = unserialize($value);
// if (!empty($value) && empty($result)) {
// $result = $value;
// }
return $result;
}
public function set($key, $value, ...$options)
{
if (!$this->checkKey($key)) {
return;
}
$value = serialize($value);
return parent::set($key, $value, $options);
}
public function setex($key, $seconds, $value)
{
$value = serialize($value);
return parent::setex($key, $seconds, $value);
}
public function setnx($key, $value)
{
$value = serialize($value);
return parent::setnx($key, $value);
}
public function hset($key, $field, $value)
{
$value = serialize($value);
return parent::hset($key, $field, $value);
}
public function hget($key, $field)
{
$value = parent::hget($key, $field);
$result = unserialize($value);
return $result;
}
private function checkKey($key)
{
try {
//格式校验, 字母
$arr = explode(':', $key);
$count = count($arr);
if ($count <= 2) {
throw_info('缓存键名不合法');
}
if (strlen($key) > 64) {
throw_info('缓存键名长度不能大于64位');
}
$modules = self::MODULES;
foreach ($arr as $k => $v) {
if (($k+1) == $count) {
continue;
}
if (!isset($modules[$v])) {
throw_info('缓存键名未配置');
}
$modules = $modules[$v];
}
return true;
} catch (\Throwable $e) {
if (YII_ENV != 'prod') {
Dingtalk::sendMessageToRobot(SYSTEM_EXCEPTIONS, "{$key}缓存键名异常".$e->getMessage(), [GUOJUENENG_MOBILE]);
}
// throw_info('系统繁忙,缓存异常');
return false;
}
}
}
二、通过train 解耦和通过方法动态组建key
FillerKey.php
<?php
namespace common\cache\keys;
trait FillerKeyTrait
{
public static function getFiller($platformType, $platformId, $day = '')
{
if (empty($day)) {
$day = date('ymd');
}
return 'filler:' . $day . $platformId . ':' . $platformType;
}
}
$key = Redis::getFiller(19,20,‘2023-12-12 09:12:23’);
三、yii2 redis初始化类直接调用 common\framework\Redis 或者读取后重启一个redis引用
return [
//common\framework\Redis
'class' => 'yii\redis\Connection',
'hostname'=>'xxxxxxxx',
'port' => 6379,
'database' => 1,
'password' => 'xxxxxx',
// 'password' => '',
];
$goodsRedis = $redis;
$goodsRedis['database'] = 4;
$goodsRedis['class'] = 'common\framework\Redis';
四、全局入口
if (!function_exists('get_cache')) {
/**
* 获取商品专用redis对象
* @return \common\framework\Redis
*/
function get_cache()
{
return \Yii::$app->goodsRedis;
}
}