通用连接池

EasySwoole通用的协程连接池管理。

组件要求

  • php: >=7.1.0
  • ext-json: *
  • easyswoole/component: ^2.2.1
  • easyswoole/spl: ^1.3
  • easyswoole/utility: ^1.1

安装方法

composer require easyswoole/pool

仓库地址

easyswoole/pool

池配置

在实例化一个连接池对象时,需要传入一个连接池配置对象EasySwoole\Pool\Config,该对象的属性如下:

配置项默认值说明备注
$intervalCheckTime30*1000定时器执行频率用于定时执行连接池对象回收,创建操作
$maxIdleTime15连接池对象最大闲置时间(秒)超过这个时间未使用的对象将会被定时器回收
$maxObjectNum20连接池最大数量每个进程最多会创建$maxObjectNum连接池对象,如果对象都在使用,则会返回空,或者等待连接空闲
$minObjectNum5连接池最小数量(热启动)当连接池对象总数低于$minObjectNum时,会自动创建连接,保持连接的活跃性,让控制器能够尽快的获取连接
$getObjectTimeout3.0获取连接池的超时时间当连接池为空时,会等待$getObjectTimeout秒,如果期间有连接空闲,则会返回连接对象,否则返回null
$extraConf额外配置信息在实例化连接池前,可把一些额外配置放到这里,例如数据库配置信息,redis配置等等
$loadAverageTime0.001负载阈值并发来临时,连接池内对象达到 maxObejctNum,此时并未达到 intervaklCheckTime 周期检测,因此设定一个 5s 负载检测,当 5s 内,取出总时间/取出连接总次数,会得到一个平均取出时间,如果小于此阈值,说明此次并发峰值非持续性,将回收 5% 的连接

池管理器

池管理器可以做全局的连接池管理,例如在EasySwooleEvent.php中的initialize中注册,然后可以在控制器中获取连接池进行获取连接:

  1. public static function initialize()
  2. {
  3. // TODO: Implement initialize() method.
  4. date_default_timezone_set('Asia/Shanghai');
  5. $config = new \EasySwoole\Pool\Config();
  6. $redisConfig1 = new \EasySwoole\Redis\Config\RedisConfig(Config::getInstance()->getConf('REDIS1'));
  7. $redisConfig2 = new \EasySwoole\Redis\Config\RedisConfig(Config::getInstance()->getConf('REDIS2'));
  8. //注册连接池管理对象
  9. \EasySwoole\Pool\Manager::getInstance()->register(new \App\Pool\RedisPool($config,$redisConfig1),'redis1');
  10. \EasySwoole\Pool\Manager::getInstance()->register(new \App\Pool\RedisPool($config,$redisConfig2),'redis2');
  11. }

控制器获取连接池连接:

  1. public function index()
  2. {
  3. //取出连接池管理对象,并getObj
  4. $redis1=\EasySwoole\Pool\Manager::getInstance()->get('redis1')->getObj();
  5. $redis2=\EasySwoole\Pool\Manager::getInstance()->get('redis1')->getObj();
  6. $redis1->set('name','仙士可');
  7. var_dump($redis1->get('name'));
  8. $redis2->set('name','仙士可2号');
  9. var_dump($redis2->get('name'));
  10. //回收对象
  11. \EasySwoole\Pool\Manager::getInstance()->get('redis1')->recycleObj($redis1);
  12. \EasySwoole\Pool\Manager::getInstance()->get('redis2')->recycleObj($redis2);
  13. }

池对象方法

方法名称参数说明备注
createObject抽象方法,创建连接对象
recycleObj$obj回收一个连接
getObjfloat $timeout = null, int $tryTimes = 3获取一个连接,超时时间$timeout,尝试获取$tryTimes次
unsetObj$obj直接释放一个连接
idleCheckint $idleTime回收超过$idleTime未出队使用的连接
itemIntervalCheckObjectInterface $item判断当前客户端是否还可用
intervalCheck回收连接,以及热启动方法,允许外部调用热启动
keepMin?int $num = null保持最小连接(热启动)
getConfig获取连接池的配置信息
status获取连接池状态信息获取当前连接池已创建,已使用,最大创建,最小创建数据
isPoolObject$obj查看$obj对象是否由该连接池创建
isInPool$obj获取当前连接是否在连接池内未使用
destroyPool销毁该连接池
reset重置该连接池
invokecallable $call,float $timeout = null获取一个连接,传入到$call回调函数中进行处理,回调结束后自动回收连接
deferfloat $timeout = null获取一个连接,协程结束后自动回收

getObj

获取一个连接池的对象:

  1. go(function (){
  2. $redisPool = new \App\Pool\RedisPool(new \EasySwoole\Pool\Config(), new \EasySwoole\Redis\Config\RedisConfig(\EasySwoole\EasySwoole\Config::getInstance()->getConf('REDIS')));
  3. $redis = $redisPool->getObj();
  4. var_dump($redis->echo('仙士可'));
  5. $redisPool->recycleObj($redis);
  6. });

通过getObj方法获取的对象,都必须调用unsetObj或者recycleObj进行回收,否则连接池对象会越来越少

unsetObj

直接释放一个连接池对象,其他协程不能再获取这个连接,而是会重新创建一个连接

释放之后,并不会立即销毁该对象,而是会在作用域结束之后销毁

recycleObj

回收一个连接对象,回收之后,其他协程可以正常获取这个连接.

回收之后,其他协程可以正常获取这个连接,但在此时,该连接还处于当前协程中,如果再次调用该连接进行数据操作,将会协程混乱,所以需要开发人员自行约束,当recycleObj不能再操作这个对象

invoke

获取一个连接,传入到$call回调函数中进行处理,回调结束后自动回收连接:

  1. go(function (){
  2. $redisPool = new \App\Pool\RedisPool(new \EasySwoole\Pool\Config(), new \EasySwoole\Redis\Config\RedisConfig(\EasySwoole\EasySwoole\Config::getInstance()->getConf('REDIS')));
  3. $redisPool->invoke(function (\EasySwoole\Redis\Redis $redis){
  4. var_dump($redis->echo('仙士可'));
  5. });
  6. });

通过该方法无需手动回收连接,在回调函数结束后,则自动回收

defer

获取一个连接,协程结束后自动回收

  1. go(function () {
  2. $redisPool = new \App\Pool\RedisPool(new \EasySwoole\Pool\Config(), new \EasySwoole\Redis\Config\RedisConfig(\EasySwoole\EasySwoole\Config::getInstance()->getConf('REDIS')));
  3. $redis = $redisPool->defer();
  4. var_dump($redis->echo('仙士可'));
  5. });

通过该方法无需手动回收连接,在协程结束后,则自动回收

需要注意的事,defer方法是协程结束后才回收,如果你当前协程运行时间过长,则会一直无法回收,直到协程结束

keepMin

保持最小连接(热启动) 由于easyswoole/pool的 当一启动服务,出现过大的并发时,可能会突然需要几十上百个连接,这个时候为了使创建连接的时间分散,可以通过调用keepMin进行预热启动连接
调用此方法后,将会预先创建n个连接,用于服务启动之后的控制器直接获取连接: 在EasySwooleEvent.php中的mainServerCreate中,当worker进程启动后,热启动连接

  1. public static function mainServerCreate(EventRegister $register)
  2. {
  3. $register->add($register::onWorkerStart,function (\swoole_server $server,int $workerId){
  4. if ($server->taskworker == false) {
  5. //每个worker进程都预创建连接
  6. \EasySwoole\Pool\Manager::getInstance()->get('redis')->keepMin(10);
  7. var_dump(\EasySwoole\Pool\Manager::getInstance()->get('redis')->status());
  8. }
  9. });
  10. // TODO: Implement mainServerCreate() method.
  11. }

将会输出:

  1. array(4) {
  2. ["created"]=>
  3. int(10)
  4. ["inuse"]=>
  5. int(0)
  6. ["max"]=>
  7. int(20)
  8. ["min"]=>
  9. int(5)
  10. }

keepMin是根据不同进程,创建不同的连接的,比如你有10个worker进程,将会输出10次,总共创建10*10=100个连接

getConfig

获取连接池的配置:

  1. $redisPool = new \App\Pool\RedisPool(new \EasySwoole\Pool\Config(), new \EasySwoole\Redis\Config\RedisConfig(\EasySwoole\EasySwoole\Config::getInstance()->getConf('REDIS')));
  2. var_dump($redisPool->getConfig());

destroyPool

销毁连接池
调用之后,连接池剩余的所有链接都会unsetObj,并且将关闭连接队列,调用之后getObj等方法都将失效:

  1. go(function (){
  2. $redisPool = new \App\Pool\RedisPool(new \EasySwoole\Pool\Config(), new \EasySwoole\Redis\Config\RedisConfig(\EasySwoole\EasySwoole\Config::getInstance()->getConf('REDIS')));
  3. var_dump($redisPool->getObj());
  4. $redisPool->destroyPool();
  5. var_dump($redisPool->getObj());
  6. });

reset

重置连接池,调用reset之后,会自动调用destroyPool销毁连接池,并在下一次getObj时重新初始化该连接池:

  1. go(function (){
  2. $redisPool = new \App\Pool\RedisPool(new \EasySwoole\Pool\Config(), new \EasySwoole\Redis\Config\RedisConfig(\EasySwoole\EasySwoole\Config::getInstance()->getConf('REDIS')));
  3. var_dump($redisPool->getObj());
  4. $redisPool->reset();
  5. var_dump($redisPool->getObj());
  6. });

status

获取连接池当前状态,调用之后将输出:

  1. array(4) {
  2. ["created"]=>
  3. int(10)
  4. ["inuse"]=>
  5. int(0)
  6. ["max"]=>
  7. int(20)
  8. ["min"]=>
  9. int(5)
  10. }

idleCheck

回收空闲超时的连接

intervalCheck

调用此方法后,将调用idleCheck和keepMin方法,用于手动回收空闲连接和手动热启动连接

  1. public function intervalCheck()
  2. {
  3. $this->idleCheck($this->getConfig()->getMaxIdleTime());
  4. $this->keepMin($this->getConfig()->getMinObjectNum());
  5. }

itemIntervalCheck

在内部定时器丢弃超时客户端(闲置了超过指定时间,就先断开)时,会触发itemIntervalCheck函数,并将客户端传入,可以实现用户自己的判断客户端是否可用的逻辑。

该函数如果返回true代表可用(默认情况) 返回false将会导致该客户端直接丢弃。

可用于:维持客户端心跳等。如orm中对其使用场景如下:维持mysql连接,减少mysql掉线 gone away的几率

  1. /**
  2. * @param MysqliClient $item
  3. * @return bool
  4. */
  5. public function itemIntervalCheck($item): bool
  6. {
  7. /*
  8. * 如果最后一次使用时间超过autoPing间隔
  9. */
  10. /** @var Config $config */
  11. $config = $this->getConfig();
  12. if($config->getAutoPing() > 0 && (time() - $item->__lastUseTime > $config->getAutoPing())){
  13. try{
  14. //执行一个sql触发活跃信息
  15. $item->rawQuery('select 1');
  16. //标记使用时间,避免被再次gc
  17. $item->__lastUseTime = time();
  18. return true;
  19. }catch (\Throwable $throwable){
  20. //异常说明该链接出错了,return 进行回收
  21. return false;
  22. }
  23. }else{
  24. return true;
  25. }
  26. }

基本使用

定义池对象

  1. class Std implements \EasySwoole\Pool\ObjectInterface {
  2. function gc()
  3. {
  4. /*
  5. * 本对象被pool执行unset的时候
  6. */
  7. }
  8. function objectRestore()
  9. {
  10. /*
  11. * 回归到连接池的时候
  12. */
  13. }
  14. function beforeUse(): ?bool
  15. {
  16. /*
  17. * 取出连接池的时候,若返回false,则当前对象被弃用回收
  18. */
  19. return true;
  20. }
  21. public function who()
  22. {
  23. return spl_object_id($this);
  24. }
  25. }

定义池

  1. class StdPool extends \EasySwoole\Pool\AbstractPool{
  2. protected function createObject()
  3. {
  4. return new Std();
  5. }
  6. }

不一定非要创建返回 EasySwoole\Pool\ObjectInterface 对象,任意类型对象均可

在pool组件版本>= 1.0.2后,提供了魔术池支持,可以快速进行定义池

  1. use \EasySwoole\Pool\MagicPool;
  2. $magic = new MagicPool(function (){
  3. return new \stdClass(); // 示例,可以返回实现了 ObjectInterface 的对象
  4. });
  5. // 注册后获取
  6. $test = $magic->getObj();
  7. // 归还
  8. $magic->recycleObj($test);

魔术池构造方法的第二个参数,可以接收一个 config(EasySwoole\Pool\Config类),用于定义池数量等配置。

简单示例

  1. $config = new \EasySwoole\Pool\Config();
  2. $pool = new StdPool($config);
  3. go(function ()use($pool){
  4. $obj = $pool->getObj();
  5. $obj2 = $pool->getObj();
  6. var_dump($obj->who());
  7. var_dump($obj2->who());
  8. });

进阶使用

基于pool实现的redis连接池

[基于pool实现的mysql连接池]()

相关仓库

easyswoole/redis-pool