easyswoole / pool
php stander lib
Installs: 85 036
Dependents: 24
Suggesters: 0
Security: 0
Stars: 9
Watchers: 2
Forks: 6
Open Issues: 2
Requires
- php: >=7.1.0
- ext-json: *
- easyswoole/component: ^2.0
- easyswoole/spl: ^2.0
- easyswoole/utility: ^1.1
Requires (Dev)
- easyswoole/phpunit: ^1.0
- easyswoole/swoole-ide-helper: ^1.0
README
Pool
通用的连接池管理。
安装
composer require easyswoole/pool
基础示例代码
定义池对象
<?php class Std implements \EasySwoole\Pool\ObjectInterface { function gc() { /* * 本对象被pool执行unset的时候 */ } function objectRestore() { /* * 回归到连接池的时候 */ } function beforeUse(): ?bool { /* * 取出连接池的时候,若返回false,则当前对象被弃用回收 */ return true; } public function who() { return spl_object_id($this); } }
定义池
<?php class StdPool extends \EasySwoole\Pool\AbstractPool { protected function createObject() { return new Std(); } }
不一定非要在创建对象方法
createObject()
中返回EasySwoole\Pool\ObjectInterface
对象,任意类型对象均可
使用
<?php $config = new \EasySwoole\Pool\Config(); $pool = new StdPool($config); go(function () use ($pool) { $obj = $pool->getObj(); $obj2 = $pool->getObj(); var_dump($obj->who()); var_dump($obj2->who()); });
Redis连接池实现示例
安装 easyswoole/redis 组件:
composer require easyswoole/redis
新增RedisPool管理器
新增文件 \App\Pool\RedisPool.php
,内容如下:
RedisPool 管理器(基于 AbstractPool
实现):
<?php /** * This file is part of EasySwoole. * * @link https://www.easyswoole.com * @document https://www.easyswoole.com * @contact https://www.easyswoole.com/Preface/contact.html * @license https://github.com/easy-swoole/easyswoole/blob/3.x/LICENSE */ namespace App\Pool; use EasySwoole\Pool\Config; use EasySwoole\Pool\AbstractPool; use EasySwoole\Redis\Config\RedisConfig; use EasySwoole\Redis\Redis; class RedisPool extends AbstractPool { protected $redisConfig; /** * 重写构造函数,为了传入 redis 配置 * RedisPool constructor. * @param Config $conf * @param RedisConfig $redisConfig * @throws \EasySwoole\Pool\Exception\Exception */ public function __construct(Config $conf,RedisConfig $redisConfig) { parent::__construct($conf); $this->redisConfig = $redisConfig; } protected function createObject() { // 根据传入的 redis 配置进行 new 一个 redis 连接对象 $redis = new Redis($this->redisConfig); return $redis; } }
或使用 RedisPool 管理器(基于 MagicPool
实现):
<?php /** * This file is part of EasySwoole. * * @link https://www.easyswoole.com * @document https://www.easyswoole.com * @contact https://www.easyswoole.com/Preface/contact.html * @license https://github.com/easy-swoole/easyswoole/blob/3.x/LICENSE */ namespace App\Pool; use EasySwoole\Pool\Config; use EasySwoole\Pool\MagicPool; use EasySwoole\Redis\Config\RedisConfig; use EasySwoole\Redis\Redis; class RedisPool extends MagicPool { /** * 重写构造函数,为了传入 redis 配置 * RedisPool constructor. * @param Config $config 连接池配置 * @param RedisConfig $redisConfig * @throws \EasySwoole\Pool\Exception\Exception */ public function __construct(Config $config,RedisConfig $redisConfig) { parent::__construct(function () use ($redisConfig) { $redis = new Redis($redisConfig); return $redis; }, $config); } }
不管是基于 AbstractPool
实现还是基于 MagicPool
实现效果是一致的。
在主进程(或 EasySwooleEvent.php
的 initialize
/ mainServerCreate
事件)中注册到池管理器 Manager
中:
<?php $config = new \EasySwoole\Pool\Config(); $redisConfig1 = new \EasySwoole\Redis\Config\RedisConfig(\EasySwoole\EasySwoole\Config::getInstance()->getConf('REDIS1')); $redisConfig2 = new \EasySwoole\Redis\Config\RedisConfig(\EasySwoole\EasySwoole\Config::getInstance()->getConf('REDIS2')); // 注册连接池管理对象 \EasySwoole\Pool\Manager::getInstance()->register(new \App\Pool\RedisPool($config, $redisConfig1), 'redis1'); \EasySwoole\Pool\Manager::getInstance()->register(new \App\Pool\RedisPool($config, $redisConfig2), 'redis2');
调用(可在控制器中全局进行调用):
<?php go(function () { // 获取 redis 连接对象 $redis1 = \EasySwoole\Pool\Manager::getInstance()->get('redis1')->getObj(); $redis2 = \EasySwoole\Pool\Manager::getInstance()->get('redis2')->getObj(); $redis1->set('name', '仙士可'); var_dump($redis1->get('name')); $redis2->set('name', '仙士可2号'); var_dump($redis2->get('name')); // 回收连接对象(将连接对象重新归还到连接池,方便后续使用) \EasySwoole\Pool\Manager::getInstance()->get('redis1')->recycleObj($redis1); \EasySwoole\Pool\Manager::getInstance()->get('redis2')->recycleObj($redis2); // 释放连接对象(将连接对象直接彻底释放,后续不再使用) // \EasySwoole\Pool\Manager::getInstance()->get('redis1')->unsetObj($redis1); // \EasySwoole\Pool\Manager::getInstance()->get('redis2')->unsetObj($redis2); });
连接池配置项
在实例化一个连接池对象时,需要传入一个连接池配置对象 EasySwoole\Pool\Config
,该对象的属性如下:
池管理器
池管理器可以做全局的连接池管理,例如在 EasySwooleEvent.php
中的 initialize
事件中注册,然后可以在控制器中获取连接池然后进行获取连接:
<?php public static function initialize() { // TODO: Implement initialize() method. date_default_timezone_set('Asia/Shanghai'); $config = new \EasySwoole\Pool\Config(); $redisConfig1 = new \EasySwoole\Redis\Config\RedisConfig(Config::getInstance()->getConf('REDIS1')); $redisConfig2 = new \EasySwoole\Redis\Config\RedisConfig(Config::getInstance()->getConf('REDIS2')); // 注册连接池管理对象 \EasySwoole\Pool\Manager::getInstance()->register(new \App\Pool\RedisPool($config,$redisConfig1), 'redis1'); \EasySwoole\Pool\Manager::getInstance()->register(new \App\Pool\RedisPool($config,$redisConfig2), 'redis2'); }
在控制器中获取连接池连接进行调用:
<?php public function index() { // 取出连接池管理对象,然后获取连接对象(getObject) $redis1 = \EasySwoole\Pool\Manager::getInstance()->get('redis1')->getObj(); $redis2 = \EasySwoole\Pool\Manager::getInstance()->get('redis1')->getObj(); $redis1->set('name','仙士可'); var_dump($redis1->get('name')); $redis2->set('name','仙士可2号'); var_dump($redis2->get('name')); // 回收连接对象(将连接对象重新归还到连接池,方便后续使用) \EasySwoole\Pool\Manager::getInstance()->get('redis1')->recycleObj($redis1); \EasySwoole\Pool\Manager::getInstance()->get('redis2')->recycleObj($redis2); // 释放连接对象(将连接对象直接彻底释放,后续不再使用) // \EasySwoole\Pool\Manager::getInstance()->get('redis1')->unsetObj($redis1); // \EasySwoole\Pool\Manager::getInstance()->get('redis2')->unsetObj($redis2); }
池对象方法
getObj
获取一个连接池的连接对象:
<?php go(function () { $redisPool = new \App\Pool\RedisPool(new \EasySwoole\Pool\Config(), new \EasySwoole\Redis\Config\RedisConfig(\EasySwoole\EasySwoole\Config::getInstance()->getConf('REDIS'))); // 获取一个连接池的对象 $redis = $redisPool->getObj(); var_dump($redis->echo('仙士可')); $redisPool->recycleObj($redis); });
::: warning
通过 getObj
方法获取的对象,都必须调用 recycleObj
或者 unsetObj
方法进行回收,否则连接池对象会越来越少。
:::
unsetObj
直接释放一个连接池的连接对象,其他协程不能再获取到这个连接对象,而是会重新创建一个连接对象
::: warning 释放之后,并不会立即销毁该对象,而是会在作用域结束之后销毁 :::
recycleObj
回收一个连接对象,回收之后,其他协程可以正常获取这个连接对象。
::: warning
回收之后,其他协程可以正常获取这个连接,但在此时,该连接还处于当前协程中,如果再次调用该连接进行数据操作,将会造成协程混乱,所以需要开发人员自行约束,当对这个连接对象进行 recycleObj
操作后不能再操作这个对象
:::
invoke
获取一个连接,传入到 $call
回调函数中进行处理,回调结束后自动回收连接:
<?php go(function () { $redisPool = new \App\Pool\RedisPool(new \EasySwoole\Pool\Config(), new \EasySwoole\Redis\Config\RedisConfig(\EasySwoole\EasySwoole\Config::getInstance()->getConf('REDIS'))); $redisPool->invoke(function (\EasySwoole\Redis\Redis $redis) { var_dump($redis->echo('仙士可')); }); });
::: warning 通过该方法无需手动回收连接,在回调函数结束后,则自动回收 :::
defer
获取一个连接,协程结束后自动回收
<?php go(function () { $redisPool = new \App\Pool\RedisPool(new \EasySwoole\Pool\Config(), new \EasySwoole\Redis\Config\RedisConfig(\EasySwoole\EasySwoole\Config::getInstance()->getConf('REDIS'))); $redis = $redisPool->defer(); var_dump($redis->echo('仙士可')); });
::: warning 通过该方法无需手动回收连接,在协程结束后,则自动回收 :::
::: warning
需要注意的事,defer
方法是协程结束后才回收,如果你当前协程运行时间过长,则会一直无法回收,直到协程结束
:::
keepMin
保持最小连接(热启动)。
由于 easyswoole/pool
当刚启动服务,出现过大的并发时,可能会突然需要几十个甚至上百个连接,这时为了让创建连接的时间分散,可以通过调用 keepMin
方法进行预热启动连接。
调用此方法后,将会预先创建 N
个连接,用于服务启动之后的控制器直接获取连接:
预热使用示例如下:
在 EasySwooleEvent.php
中的 mainServerCreate
中,当 Worker
进程启动后,热启动连接:
<?php public static function mainServerCreate(EventRegister $register) { $register->add($register::onWorkerStart,function (\swoole_server $server,int $workerId){ if ($server->taskworker == false) { //每个worker进程都预创建连接 \EasySwoole\Pool\Manager::getInstance()->get('redis')->keepMin(10); var_dump(\EasySwoole\Pool\Manager::getInstance()->get('redis')->status()); } }); }
将会输出:
array(4) {
["created"]=>
int(10)
["inuse"]=>
int(0)
["max"]=>
int(20)
["min"]=>
int(5)
}
::: warning
keepMin
是根据不同进程,创建不同的连接的,比如你有 10
个 Worker
进程,将会输出 10
次,总共创建 10 * 10 = 100
个连接
:::
getConfig
获取连接池的配置:
<?php $redisPool = new \App\Pool\RedisPool(new \EasySwoole\Pool\Config(), new \EasySwoole\Redis\Config\RedisConfig(\EasySwoole\EasySwoole\Config::getInstance()->getConf('REDIS'))); var_dump($redisPool->getConfig());
destroy
销毁连接池。
调用之后,连接池剩余的所有链接都会被执行 unsetObj
,并且将关闭连接队列,调用之后 getObj
等方法都将失效:
<?php go(function () { $redisPool = new \App\Pool\RedisPool(new \EasySwoole\Pool\Config(), new \EasySwoole\Redis\Config\RedisConfig(\EasySwoole\EasySwoole\Config::getInstance()->getConf('REDIS'))); var_dump($redisPool->getObj()); $redisPool->destroy(); var_dump($redisPool->getObj()); });
reset
重置连接池。
调用 reset
之后,会自动调用 destroy
销毁连接池,并在下一次 getObj
时重新初始化该连接池:
<?php go(function () { $redisPool = new \App\Pool\RedisPool(new \EasySwoole\Pool\Config(), new \EasySwoole\Redis\Config\RedisConfig(\EasySwoole\EasySwoole\Config::getInstance()->getConf('REDIS'))); var_dump($redisPool->getObj()); $redisPool->reset(); var_dump($redisPool->getObj()); });
status
获取连接池当前状态,调用之后将输出:
array(4) {
["created"]=>
int(10)
["inuse"]=>
int(0)
["max"]=>
int(20)
["min"]=>
int(5)
}
idleCheck
回收空闲超时的连接
intervalCheck
调用此方法后,将调用 idleCheck
和 keepMin
方法,用于手动回收空闲连接和手动热启动连接
<?php public function intervalCheck() { $this->idleCheck($this->getConfig()->getMaxIdleTime()); $this->keepMin($this->getConfig()->getMinObjectNum()); }