当前位置: 首页 > news >正文

【Swoole系列4.6】协程连接池

协程连接池

连接池这个东西即使没用过,你也应该听说过,特别是做过 Java 等其它语言开发的同学,对这玩意绝对不会陌生。今天,我们就来讲讲 Swoole 中如何应用连接池。

连接池

连接池,概念不细讲了。反正你要知道,对于一次查询来说,建立连接是非常耗时的。而连接池,则是将连接保存起来,需要用的时候直接取出来一个,不用每次都创建新的连接,从而极大地提升数据查询的效率。

Swoole 中的连接池,是基于协程的,并且也是通过 Channel 自动调度的,你不用管太多别的,只管用就是了。它为我们默认准备好了三种连接池,分别是 PDOPool、MysqliPool、RedisPool 。相信不用我多解释了,就是 MySQL 数据库和 Redis 的连接池应用。它们都支持自动断线重连,可以恢复大部分连接上下文,处于事务中的连接如果断开,是无法恢复上下文的,而且会抛出异常。如果有连接对象出现异常不可用的情况,需要调用一个 put(null) 方法,归还一个空连接以保证连接池的数量平衡。

每个连接池,都提供了四个方法。

  • get 方法获取连接(连接池未满时会创建新的连接)

  • put 方法回收连接

  • fill 方法填充连接池(提前创建连接)

  • close 关闭连接池

理论方面的东西就是这些,我们直接来看看怎么用。Mysqli 现在使用的已经越来越少了,所以我们就只看看 PDO 和 Redis 的使用。

PDO 连接池

$time = microtime(true);

\Swoole\Coroutine\run(function(){
    $pdoConfig = new \Swoole\Database\PDOConfig();
    $pdoConfig
        ->withUnixSocket('/var/lib/mysql/mysql.sock')
//        ->withHost('127.0.0.1')
//        ->withPort(3306)
        ->withDbName('test')
        ->withCharset('utf8mb4')
        ->withUsername('root')
        ->withPassword('123456');

    $pool = new \Swoole\Database\PDOPool($pdoConfig, 2);

    for($i = 4;$i--;){
        go(function()use($pool){
            $pdo = $pool->get();
            $statement = $pdo->prepare('SELECT ? + ?');
            if (!$statement) {
                throw new RuntimeException('Prepare failed');
            }
            $a = mt_rand(1, 100);
            $b = mt_rand(1, 100);
            $result = $statement->execute([$a, $b]);
            if (!$result) {
                throw new RuntimeException('Execute failed');
            }
            $result = $statement->fetchAll();
            if ($a + $b !== (int)$result[0][0]) {
                throw new RuntimeException('Bad result');
            }
            echo spl_object_id($pdo), PHP_EOL; // 打印 pdo 对象 id
            $pool->put($pdo);
        });
    }
});
echo microtime(true) - $time, PHP_EOL;
//11
//8
//8
//11
//0.0019669532775879

PDOPool 是 PDO 的连接池对象,它需要两个构造参数:第一个参数是一个 PDOConfig 对象,可以看到这个对象主要就是我们连接的参数信息;第二个参数是连接池的数量,默认是 64 ,为了方便测试,目前我们先设置成 2 。

连接池对象准备好之后,创建 4 个协程,在这些协程中使用连接池去请求 MySQL 查询,当然,并没有查询什么真的表,只是做一个简单的计算操作,如果执行或计算失败,会抛出异常。最后,我们还打印了连接对象的 ID 。

从输出的内容可以看出,连接对象只有两个,它们会来回重复使用。这就是连接池的作用,我们不必重复地创建连接对象,节省建立连接的时间。由于我们的数量比较小,看不出什么效果,大家可以加大协程数量,比如我们将 $i 改为 1024 。

// 1024 2 0.23715400695801
// 1024 10 0.13657021522522

1024 个协程,2 个连接的连接池执行的结果是 0.237 秒。10 个连接的连接池的执行时间是 0.136 秒。这个提升还是比较明显吧,不过我们的操作太简单了,如果有更复杂的查询,或者真实的业务场景,提升的效果还会更明显。

Redis 连接池

Redis 连接池的设置配置和 PDO 区别不大,只是 RedisConfig 的方法参数不同而已。

$time = microtime(true);
\Swoole\Coroutine\run(function(){
    $pool = new \Swoole\Database\RedisPool((new \Swoole\Database\RedisConfig())
        ->withHost('127.0.0.1')
        ->withPort(6379)
        ->withAuth('')
        ->withDbIndex(0)
        ->withTimeout(1)
    , 2);
    for ($n = 4; $n--;) {
        go(function () use ($pool) {
            $redis = $pool->get();
            $result = $redis->set('foo', 'bar');
            if (!$result) {
                throw new RuntimeException('Set failed');
            }
            $result = $redis->get('foo');
            if ($result !== 'bar') {
                throw new RuntimeException('Get failed');
            }
            echo spl_object_id($redis), PHP_EOL; // 打印 pdo 对象 id
            $pool->put($redis);
        });
    }
});

echo microtime(true) - $time, PHP_EOL;
//14
//16
//14
//16
//0.0018310546875

我们同样可以看出是两个相同的连接对象 ID 在切换。然后你也可以自己再调大创建的协程数量以及调整连接池数据进行测试。

连接池设置多大

连接池的数量可不是随便设置的,第一点,你不能超过对方系统所支持的连接数量。比如说你设置个 1000 ,但 MySQL 只配置 max_connections 支持 100 个连接,那么直接就会报错,这个大家可以试试。

PostgreSQL 提供的一个公式是 连接数 = ((核心数 * 2) + 有效磁盘数) ,比如你是 4核 的服务器,那么连接池数量设置为 ((4*2)+1)=9 个就可以了。

具体的内容其实还是和底层的进程、线程、协程相关的知识有关,我也没法讲得太深入,但是,一般情况下,确实不用设置太多。在 PDOPool 和 RedisPool 的构造函数中,连接数这个值是可以不用设置的,默认它会给一个 64 ,如果没有别的特殊情况,直接使用这个默认值也是没什么问题的。

总结

今天的内容也很简单吧?连接池在别的开发语言中也应用得非常广泛,但在 PHP 中确实还是比较少见的,还是那句话,转换思维,接纳更多的知识。就像你学过 Java 的话,对这个东西的理解就是完全没难度的,反过来,现在你深入的再去自己查资料更详细的了解一下连接池,那么将来看 Java 相关的项目时,也不会再因为这个问题而产生困扰了。程序语言的学习就是这样,一个通了,其它语言的实现也只是大差不差的。

测试代码:

https://github.com/zhangyue0503/swoole/blob/main/4.Swoole%E5%8D%8F%E7%A8%8B/source/4.6%E5%8D%8F%E7%A8%8B%E8%BF%9E%E6%8E%A5%E6%B1%A0.php

参考文档:

https://wiki.swoole.com/#/coroutine/conn_pool

相关文章:

  • 美的集团上半年营收1827亿:净利160亿 狠心批量裁员
  • 手机+卫星,到底有多难?
  • c++基础(九)——静态成员
  • Android中的Drawable(三)
  • Java面向对象(二)
  • 【spring】bean的生命周期
  • R语言ggplot2可视化:使用ggpubr包的ggline函数可视化分组折线图(点线图、line plot)、palette参数自定义不同分组折线图的颜色
  • Dubbo之注册与发现
  • R语言使用df函数生成F分布密度函数数据、使用plot函数可视化F分布密度函数数据(F Distribution)
  • Java--正则表达式
  • java计算机毕业设计家政服务管理源码+数据库+系统+lw文档+mybatis+运行部署
  • Java常量池理解
  • Dubbo入门介绍及学习笔记总结
  • 【SpringMVC】重定向和转向详解
  • ocr的场景应用--发票识别
  • [case10]使用RSQL实现端到端的动态查询
  • 【159天】尚学堂高琪Java300集视频精华笔记(128)
  • 2018一半小结一波
  • node-glob通配符
  • spark本地环境的搭建到运行第一个spark程序
  • 大数据与云计算学习:数据分析(二)
  • 动态规划入门(以爬楼梯为例)
  • 给github项目添加CI badge
  • 理解IaaS, PaaS, SaaS等云模型 (Cloud Models)
  • 前嗅ForeSpider采集配置界面介绍
  • 区块链分支循环
  • 如何使用Mybatis第三方插件--PageHelper实现分页操作
  • 适配iPhoneX、iPhoneXs、iPhoneXs Max、iPhoneXr 屏幕尺寸及安全区域
  • 我有几个粽子,和一个故事
  • 源码之下无秘密 ── 做最好的 Netty 源码分析教程
  • 职业生涯 一个六年开发经验的女程序员的心声。
  • 仓管云——企业云erp功能有哪些?
  • 教程:使用iPhone相机和openCV来完成3D重建(第一部分) ...
  • ​​​​​​​​​​​​​​Γ函数
  • ​2020 年大前端技术趋势解读
  • (10)ATF MMU转换表
  • (C语言)编写程序将一个4×4的数组进行顺时针旋转90度后输出。
  • (附源码)springboot 个人网页的网站 毕业设计031623
  • (附源码)ssm码农论坛 毕业设计 231126
  • (转载)微软数据挖掘算法:Microsoft 时序算法(5)
  • (自适应手机端)响应式新闻博客知识类pbootcms网站模板 自媒体运营博客网站源码下载
  • ***测试-HTTP方法
  • .NET MAUI学习笔记——2.构建第一个程序_初级篇
  • .NET与 java通用的3DES加密解密方法
  • /var/lib/dpkg/lock 锁定问题
  • ??myeclipse+tomcat
  • @ModelAttribute使用详解
  • @拔赤:Web前端开发十日谈
  • [ vulhub漏洞复现篇 ] struts2远程代码执行漏洞 S2-005 (CVE-2010-1870)
  • [1204 寻找子串位置] 解题报告
  • [AI]文心一言出圈的同时,NLP处理下的ChatGPT-4.5最新资讯
  • [AMQP Connection 127.0.0.1:5672] An unexpected connection driver error occured
  • [BUUCTF 2018]Online Tool
  • [BZOJ] 2044: 三维导弹拦截
  • [BZOJ4016][FJOI2014]最短路径树问题