diff --git a/README.md b/README.md index d7c6e0b..05b1a22 100644 --- a/README.md +++ b/README.md @@ -1 +1,118 @@ -# abstractInterface \ No newline at end of file +# PoolInterface + +``` +use EasySwoole\Component\Pool\PoolManager; +use EasySwoole\Component\Pool\TraitObjectInvoker; +use EasySwoole\Utility\Random; +use EasySwoole\Component\Pool\AbstractPoolObject; +use EasySwoole\Component\Pool\PoolObjectInterface; +use EasySwoole\Component\Pool\AbstractPool; +class test +{ + public $id; + + function __construct() + { + $this->id = Random::character(8); + } + + function fuck(){ + var_dump('this is fuck at class:'.static::class.'@id:'.$this->id); + } +} + +class test2 extends test implements PoolObjectInterface +{ + function objectRestore() + { + var_dump('this is objectRestore at class:'.static::class.'@id:'.$this->id); + } + + function gc() + { + // TODO: Implement gc() method. + } + + function beforeUse(): bool + { + // TODO: Implement beforeUse() method. + return true; + } +} + +class testPool extends AbstractPool +{ + + protected function createObject() + { + // TODO: Implement createObject() method. + return new test(); + } +} + +class testPool2 extends AbstractPool +{ + + protected function createObject() + { + // TODO: Implement createObject() method. + return new test2(); + } +} + + + +class test3 extends test +{ + use TraitObjectInvoker; +} + +class test4 extends AbstractPoolObject +{ + function finalFuck() + { + var_dump('final fuck'); + } + + function objectRestore() + { + var_dump('final objectRestore'); + } +} + +//cli下关闭pool的自动定时检查 +PoolManager::getInstance()->getDefaultConfig()->setIntervalCheckTime(0); + +go(function (){ + go(function (){ + $object = PoolManager::getInstance()->getPool(test::class)->getObj(); + $object->fuck(); + PoolManager::getInstance()->getPool(test::class)->recycleObj($object); + }); + + go(function (){ + testPool::invoke(function (test $test){ + $test->fuck(); + }); + }); + + go(function (){ + testPool2::invoke(function (test2 $test){ + $test->fuck(); + }); + }); + + go(function (){ + test3::invoke(function (test3 $test3){ + $test3->fuck(); + }); + }); + + go(function (){ + $object = PoolManager::getInstance()->getPool(test4::class)->getObj(); + $object->finalFuck(); + PoolManager::getInstance()->getPool(test4::class)->recycleObj($object); + }); +}); + +``` \ No newline at end of file diff --git a/composer.json b/composer.json index 77404d1..5d23a31 100644 --- a/composer.json +++ b/composer.json @@ -14,7 +14,8 @@ "require": { "php": ">=7.1.0", "ext-swoole":"^4.0", - "easyswoole/swoole-ide-helper": "^1.2" + "easyswoole/swoole-ide-helper": "^1.2", + "easyswoole/utility": "^1.0" }, "autoload": { "psr-4": { diff --git a/src/Pool/AbstractPool.php b/src/Pool/AbstractPool.php index ac32eb1..efd2156 100644 --- a/src/Pool/AbstractPool.php +++ b/src/Pool/AbstractPool.php @@ -12,12 +12,14 @@ use EasySwoole\Component\Pool\Exception\PoolEmpty; use EasySwoole\Component\Pool\Exception\PoolNumError; use EasySwoole\Component\Pool\Exception\PoolUnRegister; +use EasySwoole\Utility\Random; use Swoole\Coroutine\Channel; abstract class AbstractPool { private $createdNum = 0; - private $chan; + private $inuse = 0; + private $poolChannel; private $objHash = []; private $conf; /* @@ -32,7 +34,7 @@ public function __construct(PoolConf $conf) throw new PoolNumError("pool max num is small than min num for {$class} error"); } $this->conf = $conf; - $this->chan = new Channel($conf->getMaxObjectNum() + 1); + $this->poolChannel = new Channel($conf->getMaxObjectNum() + 1); if($conf->getIntervalCheckTime() > 0){ swoole_timer_tick($conf->getIntervalCheckTime(),[$this,'intervalCheck']); } @@ -47,23 +49,26 @@ public function recycleObj($obj):bool if($obj instanceof PoolObjectInterface){ $obj->objectRestore(); } - return $this->putObject($obj); - }else{ - return false; + $ret = $this->putObject($obj); + if($ret){ + $this->inuse--; + } + return true; } + return false; } - public function getObj(float $timeout = null,int $tryTimes = 3) + public function getObj(float $timeout = null,int $beforeUseTryTimes = 3) { if($timeout === null){ $timeout = $this->conf->getGetObjectTimeout(); } - if($tryTimes <= 0){ + if($beforeUseTryTimes <= 0){ return null; } //懒惰创建模式 $obj = null; - if($this->chan->isEmpty()){ + if($this->poolChannel->isEmpty()){ //如果还没有达到最大连接数,则尝试进行创建 if($this->createdNum < $this->conf->getMaxObjectNum()){ $this->createdNum++; @@ -71,31 +76,41 @@ public function getObj(float $timeout = null,int $tryTimes = 3) * 创建对象的时候,请加try,尽量不要抛出异常 */ $obj = $this->createObject(); - if(!$this->putObject($obj)){ - $this->createdNum--; + $hash = Random::character(16); + if(is_object($obj)){ + //标记手动标记一个id spl_hash 存在坑 + $obj->__objectHash = $hash; + //标记为false,才可以允许put回去队列 + $this->objHash[$hash] = false; + if(!$this->putObject($obj)){ + $this->createdNum--; + unset($this->objHash[$hash]); + } } //同样进入调度等待,理论上此处可以马上pop出来 - $obj = $this->chan->pop($timeout); + $obj = $this->poolChannel->pop($timeout); }else{ - $obj = $this->chan->pop($timeout); + $obj = $this->poolChannel->pop($timeout); } }else{ - $obj = $this->chan->pop($timeout); + $obj = $this->poolChannel->pop($timeout); } //对对象进行标记处理 if(is_object($obj)){ - $key = spl_object_hash($obj); + //上一步已经put object了,put object中设置了__objectHash + $key = $obj->__objectHash; //标记这个对象已经出队列了 $this->objHash[$key] = false; if($obj instanceof PoolObjectInterface){ //请加try,尽量不要抛出异常 $status = $obj->beforeUse(); - if($status == false){ + if($status === false){ $this->unsetObj($obj); //重新进入对象获取 - return $this->getObj($timeout,$tryTimes - 1); + return $this->getObj($timeout,$beforeUseTryTimes - 1); } } + $this->inuse++; return $obj; }else{ return null; @@ -108,15 +123,18 @@ public function getObj(float $timeout = null,int $tryTimes = 3) public function unsetObj($obj):bool { if(is_object($obj)){ - $key = spl_object_hash($obj); + if(!isset($obj->__objectHash)){ + return false; + } + $key = $obj->__objectHash; if(isset($this->objHash[$key])){ unset($this->objHash[$key]); + $this->createdNum--; if($obj instanceof PoolObjectInterface){ $obj->objectRestore(); $obj->gc(); } unset($obj); - $this->createdNum--; return true; }else{ return false; @@ -133,8 +151,8 @@ public function gcObject(int $idleTime) { $list = []; while (true){ - if(!$this->chan->isEmpty()){ - $obj = $this->chan->pop(0.001); + if(!$this->poolChannel->isEmpty()){ + $obj = $this->poolChannel->pop(0.001); if(is_object($obj)){ if(time() - $obj->last_recycle_time > $idleTime){ $this->unsetObj($obj); @@ -147,7 +165,7 @@ public function gcObject(int $idleTime) } } foreach ($list as $item){ - $this->chan->push($item); + $this->poolChannel->push($item); } } @@ -219,15 +237,28 @@ public function preLoad(?int $num = null):int protected function putObject($object):bool { if(is_object($object)){ - $hash = spl_object_hash($object); - //不在的时候说明为新对象,状态为false的时候,说明链接呗取出,允许归还 - if(!isset($this->objHash[$hash]) || ($this->objHash[$hash] == false)){ + if(!isset($object->__objectHash)){ + return false; + } + $hash = $object->__objectHash; + //不在的时候说明为其他pool对象,不允许归还,若为true,说明已经归还,禁止重复 + if(isset($this->objHash[$hash]) && ($this->objHash[$hash] == false)){ $object->last_recycle_time = time(); $this->objHash[$hash] = true; - $this->chan->push($object); + $this->poolChannel->push($object); return true; } } return false; } + + public function status() + { + return [ + 'created'=>$this->createdNum, + 'inuse'=>$this->inuse, + 'max'=>$this->getPoolConfig()->getMaxObjectNum(), + 'min'=>$this->getPoolConfig()->getMinObjectNum() + ]; + } } diff --git a/src/Pool/AbstractPoolObject.php b/src/Pool/AbstractPoolObject.php index 736a971..b274894 100644 --- a/src/Pool/AbstractPoolObject.php +++ b/src/Pool/AbstractPoolObject.php @@ -24,4 +24,9 @@ function beforeUse():bool { return true; } + + function objectRestore() + { + + } } \ No newline at end of file diff --git a/src/Pool/PoolConf.php b/src/Pool/PoolConf.php index 0a411d2..f88503a 100644 --- a/src/Pool/PoolConf.php +++ b/src/Pool/PoolConf.php @@ -20,7 +20,7 @@ class PoolConf protected $extraConf = []; - function __construct(string $class) + function __construct(?string $class = null) { $this->class = $class; } @@ -33,6 +33,11 @@ public function getClass(): string return $this->class; } + public function setClass(string $className) + { + $this->class = $className; + } + /** * @return float|int diff --git a/src/Pool/PoolManager.php b/src/Pool/PoolManager.php index 5e00201..3ebf1ee 100644 --- a/src/Pool/PoolManager.php +++ b/src/Pool/PoolManager.php @@ -16,13 +16,24 @@ class PoolManager use Singleton; private $pool = []; + private $defaultConfig; + function __construct() + { + $this->defaultConfig = new PoolConf(); + } + + function getDefaultConfig() + { + return $this->defaultConfig; + } function register(string $className, $maxNum = 20):?PoolConf { $ref = new \ReflectionClass($className); if($ref->isSubclassOf(AbstractPool::class)){ - $conf = new PoolConf($className); + $conf = clone $this->defaultConfig; + $conf->setClass($className); $conf->setMaxObjectNum($maxNum); $this->pool[$this->generateKey($className)] = $conf; return $conf; @@ -47,6 +58,21 @@ function getPool(string $className):?AbstractPool $this->pool[$key] = $obj; return $obj; } + }else if(class_exists($className)){ + if(!$this->register($className)){ + $config = clone $this->defaultConfig; + $config->setClass($className); + $pool = new class($config) extends AbstractPool{ + protected function createObject() + { + // TODO: Implement createObject() method. + $className = $this->getPoolConfig()->getClass(); + return new $className; + } + }; + $this->pool[$key] = $pool; + } + return $this->getPool($className); } return null; } diff --git a/src/Pool/TraitObjectInvoker.php b/src/Pool/TraitObjectInvoker.php new file mode 100644 index 0000000..f3ac903 --- /dev/null +++ b/src/Pool/TraitObjectInvoker.php @@ -0,0 +1,38 @@ +getPool(static::class); + if($pool instanceof AbstractPool){ + $obj = $pool->getObj($timeout); + if($obj){ + try{ + $ret = call_user_func($call,$obj); + return $ret; + }catch (\Throwable $throwable){ + throw $throwable; + }finally{ + $pool->recycleObj($obj); + } + }else{ + throw new PoolEmpty(static::class." pool is empty"); + } + }else{ + throw new PoolUnRegister(static::class." pool is unregister"); + } + } +} \ No newline at end of file