1.应用场景

主要用于学习和使用Swoole实现连接池,应用于项目开发中。

2.学习/操作

1.文档阅读

Swoole4 文档

PHP程序员内功心法-趣说进程内连接池的作用和实现 -- 推荐阅读,层层递进

2.整理输出

2.1 什么是Swoole连接池?

官网上的原话:

Swoole 从 v4.4.13 版本开始提供了内置协程连接池,本章节会说明如何使用对应的连接池。

ConnectionPool

ConnectionPool,原始连接池,基于 Channel 自动调度,支持传入任意构造器 (callable),构造器需返回一个连接对象

  • get 方法获取连接(连接池未满时会创建新的连接)
  • put 方法回收连接
  • fill 方法填充连接池(提前创建连接)
  • close 关闭连接池

Simps 框架 的 DB 组件 基于 Database 进行封装,实现了自动归还连接、事务等功能,可以进行参考或直接使用,具体可查看 Simps 文档

Database

各种数据库连接池和对象代理的高级封装,支持自动断线重连。目前包含 PDO,Mysqli,Redis 三种类型的数据库支持:

  • PDOConfig, PDOProxy, PDOPool
  • MysqliConfig, MysqliProxy, MysqliPool
  • RedisConfig, RedisProxy, RedisPool

1. MySQL 断线重连可自动恢复大部分连接上下文 (fetch 模式,已设置的 attribute,已编译的 Statement 等等),但诸如事务等上下文无法恢复,若处于事务中的连接断开,将会抛出异常,请自行评估重连的可靠性;
2. 将处于事务中的连接归还给连接池是未定义行为,开发者需要自己保证归还的连接是可重用的;
3. 若有连接对象出现异常不可重用,开发者需要调用 $pool->put(null); 归还一个空连接以保证连接池的数量平衡。

PDOPool/MysqliPool/RedisPool

用于创建连接池对象,存在两个参数,分别为对应的 Config 对象和连接池 size

$pool = new \Swoole\Database\PDOPool(Swoole\Database\PDOConfig $config, int $size);

$pool = new \Swoole\Database\MysqliPool(Swoole\Database\MysqliConfig $config, int $size);

$pool = new \Swoole\Database\RedisPool(Swoole\Database\RedisConfig $config, int $size);Copy to clipboardErrorCopied
  • 参数

    • $config

    • int $size

      • 功能:连接池数量

      • 默认值:64

      • 其它值:无

2.2 为什么需要Swoole连接池「应用场景」

连接池的作用和应用场景,这里不再详述

2.3 什么时候出现「历史发展」

Swoole的连接池「数据库连接池」主要是为了解决PHP本身不支持连接池的缺陷,当然PHP的特性「缺陷不止这一点,当然也有好处」,而开发的扩展,网络引擎,主要用于网络开发。

简单说,就是语言,技术,事物是向前不断发展的,有需求,就会有创造。

2.4 怎么实践

环境

Mac

Docker 「LNMP」

版本:

# php -v

PHP 7.4.30 (cli) (built: Aug 23 2022 15:57:53) ( NTS )

Copyright (c) The PHP Group

Zend Engine v3.4.0, Copyright (c) Zend Technologies

#

# php --re swoole

Extension [ <persistent> extension #37 swoole version 4.8.12 ] 

...

扩展:

pdo

pod_mysql

mysqli

swoole

以上扩展均安装,开启

1). pod连接池

pdo_pool.php

<?php
declare(strict_types=1);

// Fatal error: Swoole\Runtime::enableCoroutine(): must be used in PHP CLI mode in /var/www/html/app.test/public/swoole/pdo_pool.php on line 13
// CLI终端下运行

use Swoole\Coroutine;
use Swoole\Database\PDOConfig;
use Swoole\Database\PDOPool;
use Swoole\Runtime;

const N = 1024;

Runtime::enableCoroutine();
$s = microtime(true);
Coroutine\run(function () {
    $pool = new PDOPool((new PDOConfig)
        ->withHost('172.18.0.4')
        ->withPort(3306)
        // ->withUnixSocket('/tmp/mysql.sock')
        ->withDbName('app.test')
        ->withCharset('utf8mb4')
        ->withUsername('root')
        ->withPassword('root')
    );


    for ($n = N; $n--;) {
        Coroutine::create(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');
            }
            $pool->put($pdo);
        });
    }
});


$s = microtime(true) - $s;
echo 'Use ' . $s . 's for ' . N . ' queries' . PHP_EOL;
 

2). redis连接池

redis_pool.php

<?php
declare(strict_types=1);

use Swoole\Coroutine;
use Swoole\Database\RedisConfig;
use Swoole\Database\RedisPool;
use Swoole\Runtime;

const N = 1024;

Runtime::enableCoroutine();
$s = microtime(true);
Coroutine\run(function () {
    $pool = new RedisPool((new RedisConfig)
        ->withHost('172.18.0.2')
        ->withPort(6379)
        ->withAuth('')
        ->withDbIndex(0)
        ->withTimeout(1)
    );


    for ($n = N; $n--;) {
        Coroutine::create(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');
            }
            $pool->put($redis);
        });
    }
});


$s = microtime(true) - $s;
echo 'Use ' . $s . 's for ' . (N * 2) . ' queries' . PHP_EOL;
 

3). mysqli连接池

mysqli_pool.php

<?php
declare(strict_types=1);

use Swoole\Coroutine;
use Swoole\Database\MysqliConfig;
use Swoole\Database\MysqliPool;
use Swoole\Runtime;

const N = 1024;

Runtime::enableCoroutine();
$s = microtime(true);
Coroutine\run(function () {
    $pool = new MysqliPool((new MysqliConfig)
        ->withHost('172.18.0.4')
        ->withPort(3306)
        // ->withUnixSocket('/tmp/mysql.sock')
        ->withDbName('app.test')
        ->withCharset('utf8mb4')
        ->withUsername('root')
        ->withPassword('root')
    );


    for ($n = N; $n--;) {
        Coroutine::create(function () use ($pool) {
            $mysqli = $pool->get();
            $statement = $mysqli->prepare('SELECT ? + ?');
            if (!$statement) {
                throw new RuntimeException('Prepare failed');
            }
            $a = mt_rand(1, 100);
            $b = mt_rand(1, 100);
            if (!$statement->bind_param('dd', $a, $b)) {
                throw new RuntimeException('Bind param failed');
            }
            if (!$statement->execute()) {
                throw new RuntimeException('Execute failed');
            }
            if (!$statement->bind_result($result)) {
                throw new RuntimeException('Bind result failed');
            }
            if (!$statement->fetch()) {
                throw new RuntimeException('Fetch failed');
            }
            if ($a + $b !== (int)$result) {
                throw new RuntimeException('Bad result');
            }
            while ($statement->fetch()) {
                continue;
            }
            $pool->put($mysqli);
        });
    }
});


$s = microtime(true) - $s;
echo 'Use ' . $s . 's for ' . N . ' queries' . PHP_EOL;
 

执行结果 

# php swoole/pdo_pool.php

Use 0.053647041320801s for 1024 queries

# php swoole/redis_pool.php

Use 0.051202058792114s for 2048 queries

# php swoole/mysqli_pool.php

Use 0.075921058654785s for 1024 queries

#

后续补充

...

3.问题/补充

TBD

4.参考

参见上面文档列表

后续补充

...

Logo

北京人形旗下天工造物具身智能开源社区,聚焦具身天工与慧思开物两大平台

更多推荐