定制 allsilaevex/swoole-connection-pool 二次开发

按需修改功能、优化性能、对接业务系统,提供一站式技术支持

邮箱:yvsm@zunyunkeji.com | QQ:316430983 | 微信:yvsm316

allsilaevex/swoole-connection-pool

Composer 安装命令:

composer require allsilaevex/swoole-connection-pool

包简介

Swoole Connection Pool

关键字:

README 文档

README

A solid, flexible and high-performance Swoole based connection pool.

PHP Programming Language Download Package CI Status Codecov Code Coverage Psalm Type Coverage PHPStan level Psalm level License

⚙️ Installation

composer require allsilaevex/swoole-connection-pool

Requirements

Warning

Pool has not been tested with swoole.enable_preemptive_scheduler = 1. Use at your own risk!

⚡️ Quickstart

This example demonstrates the creation of a simple pool of connections to the MySQL database.

<?php

declare(strict_types=1);

require_once __DIR__ . '/vendor/autoload.php';

\Swoole\Coroutine\run(static function () {
    $connectionPoolFactory = \Allsilaevex\ConnectionPool\ConnectionPoolFactory::create(
        size: 2,
        factory: new \Allsilaevex\ConnectionPool\ConnectionFactories\PDOConnectionFactory(
            dsn: 'mysql:host=0.0.0.0;port=3306;dbname=default',
            username: 'root',
            password: 'root',
        ),
    );

    $pool = $connectionPoolFactory->instantiate();

    \Swoole\Coroutine\parallel(n: 4, fn: static function () use ($pool) {
        /** @var \PDO $connection */
        $connection = $pool->borrow();

        $result = $connection->query('select 42')->fetchColumn();

        var_dump($result);
    });
});

✨ Features

  • High-performance even in unusual cases (see Benchmarks)
  • Handling connection failure and self-recovery
  • Doesn't burden the garbage collector
  • Coverage by static analyzers (PHPStan, Psalm) and support generics
  • Out of the box connection pool provides:
    • load-dependent resizing number of connections
    • reconnection for long-lived connections
    • leaked connection detection
    • support lifetime hooks for connections
  • Metrics that can be easily stored into Prometheus and used for analysis

❓ Why should I use a connection pool?

The most obvious reason: connection pool saves time by not establishing a new connection for each request.

A less obvious reason lies in the way Swoole and coroutines work. By design, it's not possible to use the same connection simultaneously in two different coroutines, which means you must create a separate connection for each coroutine. This adds extra overhead compared to using a single connection sequentially. It can also lead to uncontrolled growth in the number of connections.

And the least obvious issue: slowdowns due to multiple context switches during execution. Context switching occurs during IO operations, including establishing a connection. Thus, the execution flow of the following code may not be obvious:

<?php

declare(strict_types=1);

\Swoole\Coroutine\run(static function () {
    \Swoole\Coroutine\go(static function () {
        echo '1' . PHP_EOL;

        $pdo = new \PDO(
            dsn: 'mysql:host=0.0.0.0;port=3306;dbname=default',
            username: 'root',
            password: 'root',
        );

        echo '2' . PHP_EOL;

        $pdo->query('select 1')->fetchAll();

        echo '4' . PHP_EOL;
    });

    \Swoole\Coroutine\go(static function () {
        echo '3' . PHP_EOL;
    });
});

// output:
// 1
// 3
// 2
// 4

What happens if a CPU-bound load appears in the second coroutine? Since coroutines are executed within the same process, they cannot parallelize the execution of CPU-bound code. Therefore, if one coroutine is executing PHP code, the others will wait. So, the execution of query will be deferred until the second coroutine finishes its work and returns control back to the first coroutine.

🚀 Benchmarks

Without connection pool:

Running 10s test @ http://0.0.0.0:11111/test
  8 threads and 64 connections
  Thread Stats   Avg      Stdev     Max   +/- Stdev
    Latency   238.48ms  301.25ms   3.06s    85.59%
    Req/Sec    55.56     54.91   353.00     92.78%
  Latency Distribution
     50%  103.29ms
     75%  328.69ms
     90%  691.90ms
     99%    1.12s
  4280 requests in 10.02s, 667.52KB read
  Non-2xx or 3xx responses: 1736
Requests/sec:    426.94
Transfer/sec:     66.59KB

With connection pool:

Running 10s test @ http://0.0.0.0:11111/test
  8 threads and 64 connections
  Thread Stats   Avg      Stdev     Max   +/- Stdev
    Latency    11.33ms    1.72ms  38.38ms   82.34%
    Req/Sec   709.47     34.52   777.00     73.12%
  Latency Distribution
     50%   11.08ms
     75%   11.86ms
     90%   12.97ms
     99%   18.03ms
  56525 requests in 10.02s, 8.19MB read
Requests/sec:   5642.51
Transfer/sec:    837.56KB

Read more about the tests in the document.

🔧 Configuration

For simplifying configuration and creating a pool, there's the ConnectionPoolFactory. The factory comes with default settings, but it's highly recommended to customize the parameters according to your specific needs.

The following example demonstrates the configuration options:

<?php

declare(strict_types=1);

require_once __DIR__ . '/vendor/autoload.php';

\Swoole\Coroutine\run(static function () {
    $connectionPoolFactory = \Allsilaevex\ConnectionPool\ConnectionPoolFactory::create(
        // Maximum number of connections in the pool
        size: 4,

        // A trivial PDO connection factory
        // For other connections, you need to define factory that implements \Allsilaevex\Pool\PoolItemFactoryInterface
        factory: new \Allsilaevex\ConnectionPool\ConnectionFactories\PDOConnectionFactory(
            dsn: 'mysql:host=0.0.0.0;port=3306;dbname=default',
            username: 'root',
            password: 'root',
        ),
    );

    // The minimum number of connections that the pool will maintain
    // Setting it to 0 means the pool will create connections only when needed
    // Setting it to MAX means the pool will always keep exactly MAX connections
    $connectionPoolFactory->setMinimumIdle(2);

    // The time during which connections can remain idle in the pool
    // After the timeout expires, connections will be destroyed until the pool size reaches the minimumIdle value
    $connectionPoolFactory->setIdleTimeoutSec(15.0);

    // Maximum connection lifetime
    // When setting this, it's recommended to consider database limits and infrastructure constraints.
    $connectionPoolFactory->setMaxLifetimeSec(60.0);

    // Maximum waiting time for reserving a connection for re-creation (see maxLifetimeSec)
    // This can be useful when all connections in the pool are constantly occupied for a long time
    // Setting it to .0 means there will be no waiting during reservation
    $connectionPoolFactory->setMaxItemReservingForUpdateWaitingTimeSec(.5);

    // The maximum waiting time for a connection from the pool during a borrow attempt
    // After this time expires, an \Allsilaevex\Pool\Exceptions\BorrowTimeoutException will be thrown
    $connectionPoolFactory->setBorrowingTimeoutSec(.1);

    // The maximum waiting time for returning a connection to the pool
    // After this time expires, the connection will be destroyed
    $connectionPoolFactory->setReturningTimeoutSec(.01);

    // If true, then connection will automatically return to the pool after the coroutine in which it was borrowed finishes execution
    // Auto return can only work with coroutine binding!
    $connectionPoolFactory->setAutoReturn(true);

    // If true, then when borrowing a connection from the pool for one coroutine, the same connection will always be returned
    $connectionPoolFactory->setBindToCoroutine(true);

    // A logger is used to signal abnormal situations
    // Any logger that implements \Psr\Log\LoggerInterface is allowed
    $connectionPoolFactory->setLogger(logger: new \Psr\Log\NullLogger());

    // Maximum time that a connection can be out of the pool without leak warnings
    $connectionPoolFactory->setLeakDetectionThresholdSec(1.0);

    // Allows adding a KeepaliveChecker that must implement the \Allsilaevex\ConnectionPool\KeepaliveCheckerInterface
    // This checker will be called at a specified interval and can trigger connection re-creation (if it returns false)
    $connectionPoolFactory->addKeepaliveChecker(
        new class () implements \Allsilaevex\ConnectionPool\KeepaliveCheckerInterface {
            public function check(mixed $connection): bool
            {
                try {
                    $connection->getAttribute(\PDO::ATTR_SERVER_INFO);
                } catch (\Throwable) {
                    return false;
                }

                return true;
            }

            public function getIntervalSec(): float
            {
                return 60.0;
            }
        },
    );

    // Allows adding a ConnectionChecker that must be callable
    // This checker will be called before connection borrowing and can trigger connection re-creation (if it returns false)
    $connectionPoolFactory->addConnectionChecker(
        static function (\PDO $connection): bool {
            try {
                return !$connection->inTransaction();
            } catch (\Throwable) {
                return false;
            }
        },
    );

    // You can specify a pool name for identifying logs, metrics, etc.
    // Or leave the field empty and the name will be generated based on the factory class
    $pool = $connectionPoolFactory->instantiate(name: 'my-pool');

    \Swoole\Coroutine\parallel(n: 8, fn: static function () use ($pool) {
        /** @var \PDO $connection */
        $connection = $pool->borrow();

        $result = $connection->query('select 42')->fetchColumn();

        var_dump($result);
    });
});

License

MIT

allsilaevex/swoole-connection-pool 适用场景与选型建议

allsilaevex/swoole-connection-pool 是一款 基于 PHP 开发的 Composer 扩展包,目前已累计 407 次下载、GitHub Stars 达 9, 最近一次更新时间为 2024 年 01 月 27 日, 在 PHP 生态内属于活跃度较高的组件。

它主要适用于以下技术方向: 「pool」 「swoole」 等业务场景。在实际项目中,围绕这些方向常见需要落地的问题包括:接口对接、性能调优、并发安全、与既有框架(Laravel / ThinkPHP / Yii / Webman 等)的兼容适配,以及生产环境的日志埋点与稳定性保障。

我们在过去多个企业项目中使用过 allsilaevex/swoole-connection-pool 或与其功能相近的方案,如果你在选型或落地过程中遇到问题,例如 版本兼容、二次改造、私有化封装、与内部系统对接、生产 BUG 排查,欢迎联系我们协助评估。

围绕 allsilaevex/swoole-connection-pool 我们能提供哪些服务?
定制开发 / 二次开发

基于 allsilaevex/swoole-connection-pool 在你已有业务上做功能扩展、字段裁剪、UI 适配、与内部账号 / 权限 / 日志系统的深度对接。

BUG 修复 & 性能优化

线上偶发问题、内存泄漏、慢查询、并发异常等排查修复;针对高流量场景做缓存、队列、索引层面的调优。

项目外包 & 长期维护

承接完整的项目从需求 → 设计 → 开发 → 上线 → 长期运维;也可按月提供技术保姆服务。

yvsm@zunyunkeji.com QQ:316430983 微信:yvsm316 西安尊云信息科技 · 专注 PHP / Go / 分布式系统研发

统计信息

  • 总下载量: 407
  • 月度下载量: 0
  • 日度下载量: 0
  • 收藏数: 9
  • 点击次数: 3
  • 依赖项目数: 0
  • 推荐数: 0

GitHub 信息

  • Stars: 9
  • Watchers: 1
  • Forks: 4
  • 开发语言: PHP

其他信息

  • 授权协议: MIT
  • 更新时间: 2024-01-27