add code
This commit is contained in:
parent
df89d1d416
commit
8938ce9cbe
|
@ -1 +1 @@
|
|||
42
|
||||
1940
|
|
@ -3,6 +3,8 @@
|
|||
define("ROOT_PATH", dirname(dirname(__DIR__)));
|
||||
|
||||
$GLOBAL_CONFIGS = [
|
||||
'db' => require_once(__DIR__ . "/db.php"),
|
||||
'dbpool' => require_once(__DIR__ . '/dbpool.php'),
|
||||
'db' => require_once(__DIR__ . "/db.php"),
|
||||
'dbpool' => require_once(__DIR__ . '/dbpool.php'),
|
||||
'redis' => require_once(__DIR__ . '/redis.php'),
|
||||
'redispool' => require_once(__DIR__ . '/redispool.php'),
|
||||
];
|
10
app/config/redis.php
Normal file
10
app/config/redis.php
Normal file
|
@ -0,0 +1,10 @@
|
|||
<?php
|
||||
|
||||
return [
|
||||
'default' => [
|
||||
'host' => '127.0.0.1',
|
||||
'port' => '6379',
|
||||
'auth' => '',
|
||||
'db' => 6
|
||||
],
|
||||
];
|
8
app/config/redispool.php
Normal file
8
app/config/redispool.php
Normal file
|
@ -0,0 +1,8 @@
|
|||
<?php
|
||||
return [
|
||||
"default" => [
|
||||
"min" => 20,
|
||||
"max" => 30,
|
||||
"idleTime" => 40
|
||||
]
|
||||
];
|
|
@ -3,11 +3,14 @@ namespace App\controller;
|
|||
use App\model\User;
|
||||
use Core\annotations\Bean;
|
||||
use Core\annotations\Value;
|
||||
use Core\annotations\Redis;
|
||||
use Core\annotations\Lock;
|
||||
use Core\annotations\DB;
|
||||
use Core\annotations\RequestMapping;
|
||||
use Core\http\Request;
|
||||
use Core\http\Response;
|
||||
use Core\init\MyDB;
|
||||
use Core\init\RedisHelper;
|
||||
use DI\Attribute\Inject;
|
||||
|
||||
/**
|
||||
|
@ -20,6 +23,7 @@ class UserController
|
|||
* @var MyDB
|
||||
*/
|
||||
private $db;
|
||||
|
||||
/**
|
||||
* @Value ()
|
||||
*/
|
||||
|
@ -55,10 +59,20 @@ class UserController
|
|||
|
||||
/**
|
||||
* @RequestMapping(value="/user/json")
|
||||
* @Redis(key="name")
|
||||
*/
|
||||
public function json()
|
||||
{
|
||||
$user = $this->user::first();
|
||||
$user = $this->db->table('user')->get();
|
||||
return ['code' => 1, 'msg' => 'success', 'data' => $user];
|
||||
}
|
||||
|
||||
/**
|
||||
* @Lock(prefix="lock",key="#0")
|
||||
* @RequestMapping(value="/user/lock/{uid:\d+}")
|
||||
*/
|
||||
public function lock( Request $request, $uid, Response $response)
|
||||
{
|
||||
return $this->db->table('user')->get();
|
||||
}
|
||||
}
|
82
core/annotationhandlers/LockHandler.php
Normal file
82
core/annotationhandlers/LockHandler.php
Normal file
|
@ -0,0 +1,82 @@
|
|||
<?php
|
||||
|
||||
namespace Core\annotationhandlers;
|
||||
|
||||
use Core\annotations\Lock;
|
||||
use Core\annotations\Redis;
|
||||
use Core\BeanFactory;
|
||||
use Core\init\DecoratorCollector;
|
||||
use Core\init\RedisHelper;
|
||||
|
||||
function getLock($self, $params)
|
||||
{//生成锁脚本
|
||||
$script = <<<LUA
|
||||
local key = KEYS[1]
|
||||
local expire = ARGV[1]
|
||||
if redis.call('setnx',key,1)==1 then
|
||||
return redis.call('expire',key,expire)
|
||||
end
|
||||
return 0
|
||||
LUA;
|
||||
return RedisHelper::eval($script, [$self->prefix . getKey($self->key, $params), $self->expire], 1);
|
||||
}
|
||||
|
||||
function delLock($self, $params)
|
||||
{//释放锁脚本
|
||||
$script = <<<LUA
|
||||
local key = KEYS[1]
|
||||
return redis.call('del',key)
|
||||
LUA;
|
||||
return RedisHelper::eval($script, [$self->prefix . getKey($self->key, $params)], 1);
|
||||
}
|
||||
|
||||
function lock($self, $params)
|
||||
{//争抢锁
|
||||
$retry = $self->retry;
|
||||
while ($retry-- > 0) {
|
||||
$get_lock = getLock($self, $params);
|
||||
if ($get_lock) {
|
||||
return true;
|
||||
break;
|
||||
}
|
||||
usleep(1000 * 200);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
function run($self, $params, $func)
|
||||
{//执行抢锁
|
||||
try {
|
||||
if (lock($self, $params)) {
|
||||
$result = call_user_func($func, ...$params);//执行业务逻辑
|
||||
delLock($self, $params);
|
||||
return $result;
|
||||
}
|
||||
return false;
|
||||
} catch (\Exception $exception) {
|
||||
delLock($self, $params);
|
||||
return 'false';
|
||||
}
|
||||
}
|
||||
|
||||
return [
|
||||
Lock::class => function (\ReflectionMethod $method, $instance, $self) {
|
||||
$d_collector = BeanFactory::getBean(DecoratorCollector::class);
|
||||
$key = get_class($instance) . "::" . $method->getName();
|
||||
$d_collector->dSet[$key] = function ($func) use ($self) { //收集装饰器 放入 装饰器收集类
|
||||
return function ($params) use ($func, $self) {
|
||||
/** @var $self Lock */
|
||||
if ($self->key != '') {
|
||||
$result = run($self, $params, $func);
|
||||
if (!$result) {
|
||||
return '服务器繁忙';
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
return call_user_func($func, ...$params);//执行业务逻辑
|
||||
};
|
||||
};
|
||||
return $instance;
|
||||
}
|
||||
|
||||
];
|
126
core/annotationhandlers/RedisHandler.php
Normal file
126
core/annotationhandlers/RedisHandler.php
Normal file
|
@ -0,0 +1,126 @@
|
|||
<?php
|
||||
|
||||
namespace Core\annotationhandlers;
|
||||
|
||||
use Core\annotations\Redis;
|
||||
use Core\BeanFactory;
|
||||
use Core\init\DecoratorCollector;
|
||||
use Core\init\RedisHelper;
|
||||
|
||||
function getKey($key, $param)
|
||||
{
|
||||
$pattern = "/^#(\w+)/i";
|
||||
if (preg_match($pattern, $key, $matches)) {
|
||||
if (isset($param[$matches[1]])) {
|
||||
if (is_string($param[$matches[1]]) || is_int($param[$matches[1]])) {
|
||||
return $param[$matches[1]];
|
||||
}
|
||||
}
|
||||
}
|
||||
return $key;
|
||||
}
|
||||
|
||||
function RedisByString(Redis $self, array $params, $func)
|
||||
{//处理string类型的数据
|
||||
$_key = $self->prefix . getKey($self->key, $params); //缓存key
|
||||
$getFromRedis = RedisHelper::get($_key);
|
||||
if ($getFromRedis) { //缓存如果有,直接返回
|
||||
return $getFromRedis;
|
||||
} else { //缓存没有,则直接执行原控制器方法,并返回
|
||||
$getData = call_user_func($func, ...$params);
|
||||
if ($self->expire > 0) {//过期时间
|
||||
RedisHelper::setex($_key, $self->expire, json_encode($getData));
|
||||
} else {
|
||||
RedisHelper::set($_key, json_encode($getData));
|
||||
}
|
||||
return $getData;
|
||||
}
|
||||
}
|
||||
|
||||
function RedisByHash(Redis $self, array $params, $func)
|
||||
{//处理hash类型的数据
|
||||
$_key = $self->prefix . getKey($self->key, $params); //缓存key
|
||||
$getFromRedis = RedisHelper::hgetall($_key);
|
||||
if ($getFromRedis) { //缓存如果有,直接返回
|
||||
if ($self->incr != '') {
|
||||
RedisHelper::hIncrBy($_key, $self->incr, 1);
|
||||
}
|
||||
return $getFromRedis;
|
||||
} else { //缓存没有,则直接执行原控制器方法,并返回
|
||||
$getData = call_user_func($func, ...$params);
|
||||
if (is_array($getData) || is_object($getData)) {
|
||||
if (is_object($getData)) {//如果是对象,转换成数组
|
||||
$getData = json_decode(json_encode($getData), 1);
|
||||
}
|
||||
$keys = implode("", array_keys($getData));
|
||||
if (preg_match("/^\d+$/", $keys)) {
|
||||
foreach ($getData as $k => $data) {
|
||||
RedisHelper::hmset($self->prefix . getKey($self->key, $data) . $k, $data);
|
||||
}
|
||||
} else {
|
||||
RedisHelper::hmset($_key, $getData);
|
||||
}
|
||||
|
||||
}
|
||||
return $getData;
|
||||
}
|
||||
}
|
||||
|
||||
function RedisByScrtedSet(Redis $self, array $params, $func)
|
||||
{//处理有序集合类型的数据
|
||||
if ($self->coroutine) {
|
||||
$chan = call_user_func($func, ...$params);
|
||||
$getData = [];
|
||||
for ($i = 0; $i < $chan->capacity; $i++) {
|
||||
$re = $chan->pop(5);
|
||||
$getData = array_merge($getData, $re);
|
||||
}
|
||||
} else {
|
||||
$getData = call_user_func($func, ...$params);
|
||||
}
|
||||
if (is_array($getData) || is_object($getData)) {
|
||||
if (is_object($getData)) {//如果是对象,转换成数组
|
||||
$getData = json_decode(json_encode($getData), 1);
|
||||
}
|
||||
foreach ($getData as $data) {
|
||||
RedisHelper::zAdd($self->prefix, $data[$self->score], $self->member . $data[$self->key]);
|
||||
}
|
||||
}
|
||||
return $getData;
|
||||
}
|
||||
|
||||
function RedisByLua($self, $params, $func)
|
||||
{//lua脚本
|
||||
return RedisHelper::eval($self->script);
|
||||
}
|
||||
|
||||
return [
|
||||
Redis::class => function (\ReflectionMethod $method, $instance, $self) {
|
||||
$d_collector = BeanFactory::getBean(DecoratorCollector::class);
|
||||
$key = get_class($instance) . "::" . $method->getName();
|
||||
$d_collector->dSet[$key] = function ($func) use ($self) { //收集装饰器 放入 装饰器收集类
|
||||
return function ($params) use ($func, $self) {
|
||||
/** @var $self Redis */
|
||||
|
||||
if ($self->script != '') {
|
||||
return RedisByLua($self, $params, $func);
|
||||
}
|
||||
if ($self->key != "") { //处理缓存
|
||||
switch ($self->type) {
|
||||
case "string":
|
||||
return RedisByString($self, $params, $func);
|
||||
case "hash":
|
||||
return RedisByHash($self, $params, $func);
|
||||
case "sortedset":
|
||||
return RedisByScrtedSet($self, $params, $func);
|
||||
default:
|
||||
return call_user_func($func, ...$params);
|
||||
}
|
||||
}
|
||||
return call_user_func($func, ...$params);
|
||||
};
|
||||
};
|
||||
return $instance;
|
||||
}
|
||||
|
||||
];
|
17
core/annotations/Lock.php
Normal file
17
core/annotations/Lock.php
Normal file
|
@ -0,0 +1,17 @@
|
|||
<?php
|
||||
|
||||
namespace Core\annotations;
|
||||
|
||||
use Doctrine\Common\Annotations\Annotation\Target;
|
||||
|
||||
/**
|
||||
* @Annotation
|
||||
* @Target({"METHOD"})
|
||||
*/
|
||||
class Lock
|
||||
{
|
||||
public $prefix = '';
|
||||
public $key = '';
|
||||
public $retry = 3;//重试次数
|
||||
public $expire = 30;//重试次数
|
||||
}
|
23
core/annotations/Redis.php
Normal file
23
core/annotations/Redis.php
Normal file
|
@ -0,0 +1,23 @@
|
|||
<?php
|
||||
|
||||
namespace Core\annotations;
|
||||
|
||||
use Doctrine\Common\Annotations\Annotation\Target;
|
||||
|
||||
/**
|
||||
* @Annotation
|
||||
* @Target({"METHOD"})
|
||||
*/
|
||||
class Redis
|
||||
{
|
||||
public $source = 'default';
|
||||
public $key = '';
|
||||
public $type = 'string';
|
||||
public $prefix = '';
|
||||
public $expire = -1;
|
||||
public $incr = '';
|
||||
public $member = '';
|
||||
public $score = '';//有序集合分数
|
||||
public $coroutine = false;//使用协程
|
||||
public $script = '';//lua脚本
|
||||
}
|
36
core/init/RedisHelper.php
Normal file
36
core/init/RedisHelper.php
Normal file
|
@ -0,0 +1,36 @@
|
|||
<?php
|
||||
namespace Core\init;
|
||||
use Core\BeanFactory;
|
||||
use Core\lib\PHPRedisPool;
|
||||
use phpDocumentor\Reflection\Types\Array_;
|
||||
use Core\annotations\Bean;
|
||||
/**
|
||||
* @Bean()
|
||||
* Class RedisHelper
|
||||
* @method static string get(string $key)
|
||||
* @method static bool set(string $key,string $value)
|
||||
* @method static bool setex(string $key,int $ttl,string $value)
|
||||
* @method static array hgetall(string $key)
|
||||
* @method static bool hmset(string $key,array $keyandvalues)
|
||||
* @method static bool zAdd(string $key, int $score, string $member)
|
||||
* @method static mixed eval($script, $args=array(),$numberKeys=0)
|
||||
*/
|
||||
class RedisHelper{
|
||||
public static function __callStatic($name, $arguments)
|
||||
{
|
||||
/** @var $pool PHPRedisPool */
|
||||
$pool=BeanFactory::getBean(PHPRedisPool::class);
|
||||
$redis_obj=$pool->getConnection();
|
||||
try{
|
||||
if(!$redis_obj) {
|
||||
return false;
|
||||
}
|
||||
return $redis_obj->redis->$name(...$arguments);
|
||||
}catch (\Exception $exception){
|
||||
return $exception->getMessage();
|
||||
}finally{
|
||||
if($redis_obj)
|
||||
$pool->close($redis_obj);
|
||||
}
|
||||
}
|
||||
}
|
27
core/lib/PHPRedisPool.php
Normal file
27
core/lib/PHPRedisPool.php
Normal file
|
@ -0,0 +1,27 @@
|
|||
<?php
|
||||
namespace Core\lib;
|
||||
|
||||
/**
|
||||
* Class PHPRedisPool
|
||||
*/
|
||||
class PHPRedisPool extends RedisPool{
|
||||
|
||||
public function __construct(int $min = 5, int $max = 10)
|
||||
{
|
||||
global $GLOBAL_CONFIGS;
|
||||
$poolconfig=$GLOBAL_CONFIGS["redispool"]["default"];
|
||||
parent::__construct($poolconfig['min'], $poolconfig['max'],$poolconfig['idleTime']);
|
||||
}
|
||||
protected function newRedis()
|
||||
{
|
||||
global $GLOBAL_CONFIGS;
|
||||
$default=$GLOBAL_CONFIGS["redis"]["default"];
|
||||
$redis=new \Redis();
|
||||
$redis->connect($default["host"],$default["port"]);
|
||||
if($default["auth"]!=""){
|
||||
$redis->auth($default["auth"]);
|
||||
}
|
||||
$redis->select($default['db'] ?? 0);
|
||||
return $redis;
|
||||
}
|
||||
}
|
75
core/lib/RedisPool.php
Normal file
75
core/lib/RedisPool.php
Normal file
|
@ -0,0 +1,75 @@
|
|||
<?php
|
||||
|
||||
namespace Core\lib;
|
||||
use Core\annotations\Bean;
|
||||
|
||||
/**
|
||||
* @Bean()
|
||||
*/
|
||||
abstract class RedisPool
|
||||
{
|
||||
private $min;
|
||||
private $max;
|
||||
private $conns;
|
||||
private $count;//当前所有连接数
|
||||
private $idleTime = 10;//连接空闲时间秒
|
||||
|
||||
abstract protected function newRedis();
|
||||
|
||||
function __construct($min = 5, $max = 10, $idleTime = 10)
|
||||
{
|
||||
$this->min = $min;
|
||||
$this->max = $max;
|
||||
$this->idleTime = $idleTime;
|
||||
$this->conns = new \Swoole\Coroutine\Channel($this->max);
|
||||
//构造方法直接初始化Redis连接
|
||||
for ($i = 0; $i < $this->min; $i++) {
|
||||
$this->addRedisToPool();//统一调用
|
||||
}
|
||||
}
|
||||
|
||||
public function getCount()
|
||||
{
|
||||
return $this->count;
|
||||
}
|
||||
|
||||
public function getConnection()
|
||||
{//取出
|
||||
if ($this->conns->isEmpty()) {
|
||||
if ($this->count < $this->max) {//连接池没满
|
||||
$this->addRedisToPool();
|
||||
$getObject = $this->conns->pop();
|
||||
} else {
|
||||
$getObject = $this->conns->pop(5);
|
||||
}
|
||||
} else {
|
||||
$getObject = $this->conns->pop();
|
||||
}
|
||||
if ($getObject)
|
||||
$getObject->usedtime = time();
|
||||
return $getObject;
|
||||
}
|
||||
|
||||
public function close($conn)
|
||||
{//放回连接
|
||||
if ($conn) {
|
||||
$this->conns->push($conn);
|
||||
}
|
||||
}
|
||||
|
||||
public function addRedisToPool()
|
||||
{ //把对象加入池
|
||||
try {
|
||||
$this->count++;
|
||||
$db = $this->newRedis();
|
||||
if (!$db) throw new \Exception("redis创建错误");
|
||||
$dbObject = new \stdClass();
|
||||
$dbObject->usedTime = time();
|
||||
$dbObject->redis = $db;
|
||||
|
||||
$this->conns->push($dbObject);
|
||||
} catch (\Exception $ex) {
|
||||
$this->count--;
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user