获取、归还与销毁
Pool 提供三种主要使用方式。对新项目建议优先使用 invoke(),只有确实需要控制连接生命周期时才直接调用 getObj()。
invoke:回调结束自动归还
use App\Pool\DemoConnection;
use EasySwoole\Pool\Manager;
$pool = Manager::getInstance()->get('demo');
if ($pool === null) {
throw new \RuntimeException('demo pool is not registered');
}
$result = $pool->invoke(
function (DemoConnection $connection): string {
return $connection->execute('select');
},
0.5
);
第二个参数是等待空闲对象的秒数。回调正常返回或抛出异常,invoke() 都会在 finally 中尝试归还对象。
不要从回调中返回 $connection 并在外部继续使用,也不要在回调内部手动归还它。回调结束后,这个对象可能已经被其他协程借走。
嵌套调用同一个池会再次借连接:
$pool->invoke(function (DemoConnection $first) use ($pool): void {
$pool->invoke(function (DemoConnection $second): void {
// 这里同时占用了两个对象。
});
});
池容量为 1 时,内层调用会等待并最终超时。连续操作同一资源时,应放在同一个回调中完成。
defer:协程结束自动归还
/** @var DemoConnection $connection */
$connection = $pool->defer(0.5);
$connection->execute('first');
$connection->execute('second');
defer() 会把对象保存到当前协程上下文,在协程结束时自动归还。同一协程对同一个池重复调用,会得到同一个对象:
$first = $pool->defer();
$second = $pool->defer();
assert($first === $second);
它适合同一请求内多层代码共享同一个连接,但要注意:
- 连接会一直占用到整个协程结束。
- 长时间运行的消费协程不适合长期持有 defer 对象。
- 不要把 defer 得到的对象交给子协程使用或归还。
- 同一协程需要同时使用两个连接时,应改用
getObj()。
getObj:手动控制
use EasySwoole\Pool\Exception\PoolEmpty;
$connection = null;
try {
$connection = $pool->getObj(0.5);
if ($connection === null) {
throw new \RuntimeException('no healthy connection');
}
$result = $connection->execute('query');
} catch (PoolEmpty $error) {
// 池已达到上限,并且等待 0.5 秒后仍没有空闲对象。
throw new \RuntimeException('service is busy', 0, $error);
} finally {
if ($connection !== null) {
$pool->recycleObj($connection);
}
}
getObj(?float $timeout = null, int $tryTimes = 3) 的结果需要区分:
- 池耗尽且等待超时:抛出
PoolEmpty。 beforeUse()返回false且重试耗尽:可能返回null。- 创建连接失败:
createObject()的异常直接抛出。 - 池已经销毁:抛出 Pool 组件的
Exception。
tryTimes 用于借出前健康检查失败后的重试,不是创建连接失败后的重试次数。
recycleObj:正常归还
$pool->recycleObj($connection);
$connection = null;
归还时 Pool 会先执行 objectRestore(),然后把对象放回 Channel。归还完成后必须放弃当前引用,不能继续操作对象。
归还外部创建的对象、重复归还对象,或者归还已经在池中的对象时,通常会返回 false。
unsetObj:丢弃损坏对象
当业务操作已经确认连接损坏,不应再把它放回池中:
$connection = $pool->getObj();
$reusable = true;
try {
$connection->execute('query');
} catch (BrokenConnectionException $error) {
$reusable = false;
$pool->unsetObj($connection);
throw $error;
} finally {
if ($reusable) {
$pool->recycleObj($connection);
}
}
unsetObj() 会从池的对象登记中删除它、调用 gc() 并释放一个容量名额。仅执行 PHP 的 unset($connection) 不会通知连接池释放容量。
超时范围
getObj(0.5) 的超时只限制从 Swoole Channel 等待对象的时间,不包含:
createObject()建立连接的时间。beforeUse()探活时间。- 业务查询或网络读写时间。
objectRestore()和gc()时间。
因此客户端本身还要分别设置连接超时、读写超时和探活超时。