add code
This commit is contained in:
parent
df89d1d416
commit
8938ce9cbe
|
@ -1 +1 @@
|
||||||
42
|
1940
|
|
@ -5,4 +5,6 @@ define("ROOT_PATH", dirname(dirname(__DIR__)));
|
||||||
$GLOBAL_CONFIGS = [
|
$GLOBAL_CONFIGS = [
|
||||||
'db' => require_once(__DIR__ . "/db.php"),
|
'db' => require_once(__DIR__ . "/db.php"),
|
||||||
'dbpool' => require_once(__DIR__ . '/dbpool.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 App\model\User;
|
||||||
use Core\annotations\Bean;
|
use Core\annotations\Bean;
|
||||||
use Core\annotations\Value;
|
use Core\annotations\Value;
|
||||||
|
use Core\annotations\Redis;
|
||||||
|
use Core\annotations\Lock;
|
||||||
use Core\annotations\DB;
|
use Core\annotations\DB;
|
||||||
use Core\annotations\RequestMapping;
|
use Core\annotations\RequestMapping;
|
||||||
use Core\http\Request;
|
use Core\http\Request;
|
||||||
use Core\http\Response;
|
use Core\http\Response;
|
||||||
use Core\init\MyDB;
|
use Core\init\MyDB;
|
||||||
|
use Core\init\RedisHelper;
|
||||||
use DI\Attribute\Inject;
|
use DI\Attribute\Inject;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -20,6 +23,7 @@ class UserController
|
||||||
* @var MyDB
|
* @var MyDB
|
||||||
*/
|
*/
|
||||||
private $db;
|
private $db;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @Value ()
|
* @Value ()
|
||||||
*/
|
*/
|
||||||
|
@ -55,10 +59,20 @@ class UserController
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @RequestMapping(value="/user/json")
|
* @RequestMapping(value="/user/json")
|
||||||
|
* @Redis(key="name")
|
||||||
*/
|
*/
|
||||||
public function json()
|
public function json()
|
||||||
{
|
{
|
||||||
$user = $this->user::first();
|
$user = $this->db->table('user')->get();
|
||||||
return ['code' => 1, 'msg' => 'success', 'data' => $user];
|
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