Swoole使用PDO连接池操作数据库

<?php
declare(strict_types=1);
ini_set('display_errors', 'On');
error_reporting(-1);
PHP_SAPI !== 'cli' && exit('脚本只能在命令行执行');

/**
 * 查询用户数据
 *
 * @param int $uid 用户UID
 * @return array 用户数据
 * @throws Swoole\ExitException
 */
function query(int $uid): array
{
    // 从当前请求的协程上下文里取出PDO实例
    $pdo = Swoole\Coroutine::getContext()['pdo'];

    // 构造SQL语句及参数
    $query = 'SELECT `name`, `gender`, `birth` FROM `prefix_user` WHERE `uid` = ? LIMIT 1';
    $args = [$uid];

    // 预处理SQL语句
    $statement = $pdo->prepare($query);
    $statement || throw new Swoole\ExitException('Prepare failed');

    // 执行SQL语句
    $result = $statement->execute($args);
    $result || throw new Swoole\ExitException('Execute failed');

    $rows = $statement->fetchAll(PDO::FETCH_ASSOC);

    return $rows[0] ?? [];
}

Swoole\Coroutine::set(['hook_flags' => SWOOLE_HOOK_ALL | SWOOLE_HOOK_CURL]); // 一键协程化

Swoole\Coroutine\run(function () {
    $server = new Swoole\Coroutine\Http\Server('localhost', 9502, false); // 创建HTTP服务器

    // 创建PDO配置对象
    $config = new Swoole\Database\PDOConfig();
    $config->withHost('localhost');
    $config->withPort(3306);
    $config->withDbname('test_db');
    $config->withCharset('utf8mb4');
    $config->withUsername('********');
    $config->withPassword('********');

    // 创建PDO连接池,并指定连接池中PDO实例数量为64个(缺省值也是64)
    // $pool = new Swoole\Database\PDOPool($config, 64);
    $pool = new Swoole\Database\PDOPool($config, 3); // 测试PDO连接池没有PDO实例(只取出不放回)

    // 处理任意请求
    $server->handle('/', function (Swoole\Http\Request $request, Swoole\Http\Response $response) use ($pool) {
        Swoole\Coroutine::create(static function () use ($request, $response, $pool): void {
            $method = $request->getMethod(); // 获取当前请求的请求方式
            ($method !== 'POST' && $method !== 'GET') && throw new Swoole\ExitException('Error');

            try {
                $pdo = $pool->get(); // 从PDO连接池里获取一个PDO实例(连接数据库失败会抛出异常)
            } catch (PDOException $e) {
                throw new Swoole\ExitException($e->getMessage());
            }

            // 将PDO实例放回PDO连接池
            defer(static function () use ($pool, $pdo) {
                $pool->put($pdo);
                echo 'PDO实例已放回PDO连接池' . PHP_EOL;
            });

            // 把PDO实例放入本次请求的协程上下文,以便在其它地方使用PDO实例
            Swoole\Coroutine::getContext()['pdo'] = $pdo;

            try {
                $row = query(9527);
            } catch (Swoole\ExitException) {
                $row = [];
            }

            if (isset($row['name'], $row['gender'], $row['birth'])) {
                $html = "俺叫{$row['name']}({$row['gender']}),出生于{$row['birth']}年。";
            } else {
                $html = '_(:з」∠)_';
            }

            $response->header('Content-Type', 'text/html; charset=utf-8');
            $response->end($html); // 俺叫张三(男),出生于2003年。
        });
    });

    $server->start(); // 启动HTTP服务器
});


//========== 总结 ==========//
// 1、处理函数在最后必须将PDO实例放回PDO连接池,否则一旦PDO连接池里没有PDO实例将无法处理请求(表现为一直处于加载中)。
//    推荐做法是从PDO连接池里取出PDO实例后立即使用defer()函数将实例放回连接池,避免后面忘记写放回去代码。

Copyright © 2024 码农人生. All Rights Reserved