开头语

Hyperf官网介绍了为什么要使用连接池及连接池的简单案例,但是对新手却不太友好,不过我还是想基于官方文档,来做一篇更深的讲解。

先来看看官方文档对于连接池的介绍

为什么要使用连接池

当并发量很低的时候,连接可以临时建立,但当服务吞吐达到几百、几千的时候,频繁 建立连接 Connect销毁连接 Close 就有可能会成为服务的一个瓶颈,那么当服务启动的时候,先建立好若干个连接并存放于一个队列中,当需要使用时从队列中取出一个并使用,使用完后再反还到队列去,而对这个队列数据结构进行维护的,就是连接池。

简单来说就是要提升性能,减少没必要的开销,提前建立好数据库的链接并放到池子里,后边再用的时候直接从里边取就好了。

传统模式请求示意图:

连接池模式请求示意图:

连接池模式我觉得可以理解为设计模式中的注册树模式,注册树模式是项目启动的时候把实例化的类放到数组里,随用随取,而连接池模式是把数据库连接放到连接池里,随用随取,只不过两者不同的是连接池有数量限制。

官方给的自定义连接池简单案例

定义一个连接池首先需要实现一个继承了 Hyperf\Pool\Pool 的子类并实现抽象方法 createConnection,并返回一个实现了 Hyperf\Contract\ConnectionInterface 接口的对象,这样您创建的连接池对象就已经完成了,如下示例:

<?php
namespace App\Pool;use Hyperf\Contract\ConnectionInterface;
use Hyperf\Pool\Pool;class MyConnectionPool extends Pool
{public function createConnection(): ConnectionInterface{return new MyConnection();}
}

这样便可以通过对实例化后的 MyConnectionPool 对象调用 get(): ConnectionInterface 和 release(ConnectionInterface $connection): void 方法执行连接的取用和归还了。

再看看看连接池定义后的简单使用:

<?phpuse Hyperf\Pool\SimplePool\PoolFactory;
use Swoole\Coroutine\Http\Client;$factory = $container->get(PoolFactory::class);$pool = $factory->get('your pool name', function () use ($host, $port, $ssl) {return new Client($host, $port, $ssl);
}, ['max_connections' => 50
]);$connection = $pool->get();$client = $connection->getConnection(); // 即上述 Client.// Do something.$connection->release();

这里我忍不住想吐槽一下,没错,把两个官方提供的 简单案例看完,更懵了。所以为了解惑,我便看了一下Hyperf框架自带的数据库Db连接池DbPool的源码。

咱们现在跟着Hyperf的源码看一下:

首先来看数据库的配置文件databases.php:

return ['default' => ['driver' => env('DB_DRIVER', 'mysql'),'host' => env('DB_HOST', '172.28.48.1'),'port' => env('DB_PORT', 3306),'database' => env('DB_DATABASE', 'seckill'),'username' => env('DB_USERNAME', 'root'),'password' => env('DB_PASSWORD', '123456'),'charset' => env('DB_CHARSET', 'utf8mb4'),'collation' => env('DB_COLLATION', 'utf8mb4_unicode_ci'),'prefix' => env('DB_PREFIX', ''),//上边几项关于数据库的基本配置咱们就不提了,主要看连接池的配置'pool' => ['min_connections' => 1,//最小连接数'max_connections' => 50,//最大连接数'connect_timeout' => 10.0,//连接超时时间'wait_timeout' => 10.0,//等待时间,这个框架默认的是3秒,我进行压测的时候修改成了10秒'heartbeat' => -1,'max_idle_time' => (float) env('DB_MAX_IDLE_TIME', 60),],......],
];

配置文件中关于连接池的配置我已经注释了出来,我希望你能熟悉这些配置,后边会用到,咱们现在来看看关于框架自带的关于DbPool连接池的源码,注意我在源码中的注释,这很重要:

<?phpdeclare(strict_types=1);
/*** This file is part of Hyperf.** @link     https://www.hyperf.io* @document https://hyperf.wiki* @contact  group@hyperf.io* @license  https://github.com/hyperf/hyperf/blob/master/LICENSE*/
namespace Hyperf\DbConnection\Pool;use Hyperf\Contract\ConfigInterface;
use Hyperf\Contract\ConnectionInterface;
use Hyperf\DbConnection\Connection;
use Hyperf\DbConnection\Frequency;
use Hyperf\Pool\Pool;
use Hyperf\Utils\Arr;
use Psr\Container\ContainerInterface;//继承了Pool类
class DbPool extends Pool
{protected $name;//可以理解为配置文件中的键值protected $config;public function __construct(ContainerInterface $container, string $name){$this->name = $name;//获取config实例$config = $container->get(ConfigInterface::class);//从这里大概能看出来,$key应该是databases.php配置文件中的某个key$key = sprintf('databases.%s', $this->name);//如果这个key不存在,会抛出异常if (! $config->has($key)) {throw new \InvalidArgumentException(sprintf('config[%s] is not exist!', $key));}// Rewrite the `name` of the configuration item to ensure that the model query builder gets the right connection.//重写配置项的“name”,以确保模型查询生成器获得正确的连接$config->set("{$key}.name", $name);//获取配置$this->config = $config->get($key);//从配置中提取关于pool的配置,也就是咱们在刚刚的配置文件databases.php中重点提到的$options = Arr::get($this->config, 'pool', []);//这行可以忽略$this->frequency = make(Frequency::class, [$this]);//调用了父类Pool的构造方法parent::__construct($container, $options);}public function getName(): string{return $this->name;}//创建连接protected function createConnection(): ConnectionInterface{return new Connection($this->container, $this, $this->config);}
}

还记不记得前边官方文档提到的自定义连接池的使用,忘记的话可以回到顶部看一下,我在这里再简单提一下,自定义连接池MyConnectionPool 需要继承Pool类,这实例化的 MyConnectionPool 对象通过调用 get():ConnectionInterface 和 release(ConnectionInterface $connection): void 方法就可以执行连接的取用和归还了。而这两个方法就来自于父类Pool。

现在就来看下Pool类的这两个方法:

 public function get(): ConnectionInterface{$connection = $this->getConnection();try {if ($this->frequency instanceof FrequencyInterface) {$this->frequency->hit();}if ($this->frequency instanceof LowFrequencyInterface) {if ($this->frequency->isLowFrequency()) {$this->flush();}}} catch (\Throwable $exception) {if ($this->container->has(StdoutLoggerInterface::class) && $logger = $this->container->get(StdoutLoggerInterface::class)) {$logger->error((string) $exception);}}return $connection;}public function release(ConnectionInterface $connection): void{$this->channel->push($connection);}

release方法很简单,释放连接。主要讲解一下get方法。

get方法主要调用了getConnection方法,来看下getConnection源码,注意看注释:

private function getConnection(): ConnectionInterface{//获取channel的长度$num = $this->getConnectionsInChannel();try {//如果channel长度为0并且连接数小于最大连接数,则执行连接数+1,创建一个新的连接if ($num === 0 && $this->currentConnections < $this->option->getMaxConnections()) {++$this->currentConnections;return $this->createConnection();}} catch (Throwable $throwable) {//如果有异常,连接数-1--$this->currentConnections;throw $throwable;}//$this->option->getWaitTimeout()获取的是配置文件中的wait_timeout(等待时间),如果连接池满了,并且在等待时间内没有获取到连接,就会抛出异常$connection = $this->channel->pop($this->option->getWaitTimeout());if (! $connection instanceof ConnectionInterface) {throw new RuntimeException('Connection pool exhausted. Cannot establish new connection before wait_timeout.');}return $connection;}

看到了get方法的最后,你也许能明白我进行压测的时候,要把wait_timeout改为10s的原因。我按照上边配置文件中的配置,用jmeter进行了10000并发的压测,就会抛出“Connection pool exhausted. Cannot establish new connection before wait_timeout.”这个异常。

现在咱们来看下DbPool的使用,为了不动源码,我新建了一个自定义的TestPool,继承DbPool,然后通过单例模式进行对DbPool 的调用:

<?php
namespace App\Pool;use Hyperf\DbConnection\Pool\DbPool;
use phpDocumentor\Reflection\Types\Self_;
use Psr\Container\ContainerInterface;class TestPool extends DbPool{private static $_instance;public function __construct(ContainerInterface $container, string $name){parent::__construct($container, $name);}static function getInstance(ContainerInterface $container,string $name){if (!self::$_instance){self::$_instance = new self($container,$name);}return self::$_instance;}
}

然后在控制器中调用TestPool:

$TestPool = TestPool::getInstance($this->container, 'default');
echo $TestPool->getCurrentConnections() . "\n";//获取当前连接数
echo "channel:".$TestPool->getConnectionsInChannel() . "\n";//获取channel长度
try {$connection = $TestPool->get();$client = $connection->getConnection();$res = $client->table('wolive_visiter')->limit(10)->get();$connection->release();return ['res_count' => count($res)];
} catch (Exception $exception) {throwException($exception);
}

Hyperf中连接池的使用大概就是这样了

Hyperf连接池详解相关推荐

  1. python requests 异步调用_构建高效的python requests长连接池详解

    前文: 最近在搞全网的CDN刷新系统,在性能调优时遇到了requests长连接的一个问题,以前关注过长连接太多造成浪费的问题,但因为系统都是分布式扩展的,针对这种各别问题就懒得改动了. 现在开发的缓存 ...

  2. Spring Boot 使用 Druid 连接池详解

    Spring Boot 使用 Druid 连接池详解 Alibaba Druid 是一个 JDBC 组件库,包含数据库连接池.SQL Parser 等组件,被大量业务和技术产品使用或集成,经历过严苛的 ...

  3. jedis连接池详解(Redis)

    转自:http://tianxingzhe.blog.51cto.com/3390077/1684306 原子性(atomicity): 一个事务是一个不可分割的最小工作单位,事务中包括的诸操作要么都 ...

  4. java连接池详解与自定义es连接池

    目录 1 版本选择 2 依赖选择 3 使用commons-pool构造连接池 3.1 pom.xml 3.2 对象池类 GenericObjectPool普通对象池 GenericKeyedObjec ...

  5. Redis Lettuce客户端异步连接池详解

    前言 异步/非阻塞编程模型需要非阻塞API才能获得Redis连接.阻塞的连接池很容易导致阻塞事件循环并阻止您的应用程序进行处理的状态.Lettuce带有异步,非阻塞池实现,可与Lettuces异步连接 ...

  6. Mysql连接池详解——原理部分

    引言 为什么要使用连接池 线程池如何配合连接池使用, 连接池和线程池数量 不能根据经验值直接设置,需要根据io同步的具体时间去测试得到最优的值 同步连接池和异步连接池的区别 连接池的扩展 一.池化技术 ...

  7. 干货 | Tomcat 连接数与线程池详解

    转载自  干货 | Tomcat 连接数与线程池详解 前言 在使用tomcat时,经常会遇到连接数.线程数之类的配置问题,要真正理解这些概念,必须先了解Tomcat的连接器(Connector). 在 ...

  8. JDBC中C3PO数据库连接池详解

    -----------------------------------------------------JDBC中C3PO数据库连接池详解------------------------------ ...

  9. Java线程池详解学习:ThreadPoolExecutor

    Java线程池详解学习:ThreadPoolExecutor Java的源码下载参考这篇文章:Java源码下载和阅读(JDK1.8) - zhangpeterx的博客 在源码的目录java/util/ ...

最新文章

  1. mybatis源码分析之事务管理器
  2. redis geohash 学习笔记
  3. 《经济学人》也谈 Python:它会是我们的未来吗?
  4. FPGA的设计艺术(14)使用函数和任务提升逻辑的可重用性
  5. 在.NET 3.5 平台上使用LINQ to SQL创建三层/多层Web应用系统(Part2) 转
  6. 【CentOS 7】 yum源安装mysql5.6
  7. mysql如何让表建立连接吗_MySQL 表与表之间建立关系
  8. 二次优化问题dfp_MATLAB优化问题应用实例讲解
  9. Linux学习8-CentOS部署自己本地的django项目
  10. hnu暑期实训之487-3279 字符串处理
  11. 基于java 海康视频监控 jar包运行
  12. roboguide程序导出_FANUC ROBOGUIDE下载-FANUC ROBOGUIDE(发那科机器人编程软件)下载v9.1-西西软件下载...
  13. 宿命传说2之女神召唤java_开局:召唤金色暗影
  14. 《黑客秘笈——渗透测试实用指南》读书笔记(1)
  15. iOS-app更新和强制更新
  16. 【IDEA】IDEA的高级Debug技巧
  17. 深度学习相关基础理论
  18. android 9.0的模拟器,exagear模拟器最新版安卓9.0
  19. C语言见缝插针游戏,见缝插针游戏,见缝插针游戏图片
  20. 为什么收藏了这么多3D游戏建模教程,还是没达到可以就业接包的水准?

热门文章

  1. 知道怎样读书和选书?
  2. 使用VS在64位平台下编译代码,生成64位dll
  3. python初学者:打印9*9乘法表的三种方法
  4. 安卓Dialog 设置margin属性没有用
  5. Java咖啡馆---叹咖啡
  6. Python3常用其他API速查手册(持续更新ing...)
  7. 百度天天快照知识宝典
  8. 传马化腾过问“露露事件”启动反腐 腾讯暂无回应
  9. nvm使用 use 命令失效
  10. 【NEUQ RM SI战队项目开源】gazebo仿真开源