Manager 与框架接入
EasySwoole\Pool\Manager 是当前进程内的连接池注册表。业务代码可以用名称取得连接池,不需要到处传递池实例。
注册与获取
use App\Pool\DemoPool;
use EasySwoole\Pool\Config;
use EasySwoole\Pool\Manager;
$pool = new DemoPool(new Config([
'minObjectNum' => 2,
'maxObjectNum' => 10,
]));
Manager::getInstance()->register($pool, 'demo');
$registeredPool = Manager::getInstance()->get('demo');
if ($registeredPool === null) {
throw new \RuntimeException('demo pool is not registered');
}
不传名称时,Manager 使用连接池类名作为键:
Manager::getInstance()->register($pool);
$pool = Manager::getInstance()->get(DemoPool::class);
明确使用短名称通常更方便切换多套配置,例如 redis.default、redis.cache 和 database.reporting。
在全局初始化事件中注册
编辑项目根目录的 EasySwooleEvent.php,在框架全局 initialize() 事件中创建并注册连接池:
<?php
use App\Pool\DemoPool;
use EasySwoole\EasySwoole\AbstractInterface\Event;
use EasySwoole\EasySwoole\Swoole\EventRegister;
use EasySwoole\Pool\Config;
use EasySwoole\Pool\Manager;
class EasySwooleEvent implements Event
{
public static function initialize(): void
{
$config = new Config([
'minObjectNum' => 2,
'maxObjectNum' => 10,
'getObjectTimeout' => 1.0,
]);
Manager::getInstance()->register(
new DemoPool($config),
'demo'
);
}
public static function mainServerCreate(EventRegister $register): void
{
$register->add(
EventRegister::onWorkerStart,
function (\Swoole\Server $server, int $workerId): void {
if ($server->taskworker) {
return;
}
// 注册已经在 initialize() 完成;这里只做 Worker 预热。
$pool = Manager::getInstance()->get('demo');
if ($pool !== null) {
go(static function () use ($pool): void {
$pool->keepMin();
});
}
}
);
}
}
initialize() 在框架全局初始化阶段执行。这里的 new DemoPool() 和 register() 只建立 PHP 配置对象与注册关系,不会立即建立数据库、Redis 或其他网络连接,因此可以在全局初始化事件中完成。
Swoole 创建 Worker 后,每个进程会拥有自己的一份 Manager 和 Pool。真正的连接会在该进程第一次 getObj()、invoke()、defer() 或 keepMin() 时建立,不会跨进程共用同一个连接。
可选的 Worker 预热
注册放在 initialize();预热则是可选项。完全删除下面的 onWorkerStart 代码,连接池依然可以正常使用,只会在首次业务调用时按需创建连接。
只有希望减少第一批请求的建连延迟时,才在 onWorkerStart 后的协程中调用 keepMin()。TaskWorker 不使用该池时可以跳过预热:
if ($server->taskworker) {
return;
}
即使跳过预热,TaskWorker 仍然继承了注册信息。如果它后来调用连接池,连接会按需创建。HTTP Worker 和 TaskWorker 都使用连接时,要把两类进程的连接数一起计入下游连接预算。
在控制器中使用
<?php
namespace App\HttpController;
use App\Pool\DemoConnection;
use EasySwoole\Http\AbstractInterface\Controller;
use EasySwoole\Pool\Manager;
final class PoolDemo extends Controller
{
public function index(): void
{
$pool = Manager::getInstance()->get('demo');
if ($pool === null) {
$this->response()->withStatus(503);
$this->writeJson(503, null, 'connection pool is unavailable');
return;
}
$result = $pool->invoke(
fn (DemoConnection $connection) => $connection->execute('ping')
);
$this->writeJson(200, ['result' => $result]);
}
}
线上代码还应捕获 PoolEmpty 和客户端异常,把池繁忙与下游故障转换成合适的 HTTP 响应,并记录日志和监控指标。
同名注册的注意事项
再次注册同名池会直接替换 Manager 中的引用,不会自动销毁旧池:
Manager::getInstance()->register($newPool, 'demo');
运行中直接替换可能遗留旧 Timer 和连接。需要更换连接池时,应先停止新请求、等待旧连接归还,再销毁旧池并注册新实例。
Manager::resetAll() 会对已注册的池执行 destroy(),不会删除注册名称,也不会把这些池恢复为可用状态。若要继续使用某个实例,需要对该池调用 reset(),但常驻服务更建议在安全的进程生命周期中重建池。