连接对象与生命周期
连接池只负责调度对象,不知道如何连接数据库、如何判断 Redis 是否断线,也不知道业务使用后需要重置哪些状态。这些行为由连接对象通过 ObjectInterface 告诉连接池。
ObjectInterface
所有放入 Pool 3.x 的对象都必须实现:
namespace EasySwoole\Pool;
interface ObjectInterface
{
public function gc();
public function objectRestore();
public function beforeUse(): bool;
public function intervalCheck(): bool;
}
四个方法的调用时机如下:
createObject()
↓
对象进入空闲队列
↓
beforeUse() ── false/异常 ──→ gc()
↓ true
业务协程使用对象
↓
objectRestore() ── 异常 ──→ gc()
↓
对象回到空闲队列
↓
intervalCheck() ─ false/异常 → gc()
createObject:创建对象
继承 AbstractPool 后必须实现 createObject()。这里负责建立真实连接,失败时直接抛出异常:
use EasySwoole\Pool\AbstractPool;
use EasySwoole\Pool\ObjectInterface;
final class ApiClientPool extends AbstractPool
{
protected function createObject(): ObjectInterface
{
$client = new ApiClient('https://api.example.com');
$client->connect();
return $client;
}
}
不要捕获异常后返回一个未连接的对象,也不要返回 null。组件会在创建失败时释放预占的容量,然后把异常交给调用方处理。
beforeUse:借出前探活
对象从空闲队列取出后,Pool 会先调用 beforeUse():
public function beforeUse(): bool
{
return $this->client->ping();
}
- 返回
true:把对象交给调用方。 - 返回
false:执行gc()丢弃对象,然后尝试获取其他对象。 - 抛出异常:丢弃当前对象;重试次数耗尽后向调用方抛出异常。
探活应有较短的客户端超时。getObj(1.0) 的 1 秒只限制等待 Channel 的时间,不限制 ping() 自身耗时。
objectRestore:归还前恢复状态
归还对象时会调用 objectRestore()。这里用于清除上一次业务留下的状态:
public function objectRestore(): void
{
if ($this->client->inTransaction()) {
$this->client->rollBack();
}
$this->client->clearRequestContext();
}
数据库事务、Redis 事务或订阅状态、HTTP 客户端请求头等都不能泄漏给下一个协程。如果无法恢复到安全状态,应抛出异常,让 Pool 丢弃该对象。
intervalCheck:空闲时健康检查
Pool 会定时从空闲队列中取出一批对象并调用:
public function intervalCheck(): bool
{
return $this->client->ping();
}
返回 false 或抛出异常时,对象会执行 gc() 并从池中移除。这个方法只检查空闲对象;已借出的对象不会被后台检查。
gc:关闭底层资源
对象失效、连接池缩容或连接池销毁时会调用:
public function gc(): void
{
$this->client->close();
}
gc() 是连接池定义的资源清理回调,不是 PHP 的析构函数。调用后 PHP 变量可能仍然引用这个对象,但底层连接已经不可再使用。
清理方法应尽量满足两个条件:
- 即使连接已经断开,再次清理也不会产生严重错误。
- 不执行长时间阻塞操作,避免拖慢回收和健康检查。
为第三方客户端编写适配器
第三方客户端通常没有实现 ObjectInterface,可以用适配器包装:
final class ClientConnection implements \EasySwoole\Pool\ObjectInterface
{
public function __construct(
private ThirdPartyClient $client
) {
}
public function client(): ThirdPartyClient
{
return $this->client;
}
public function beforeUse(): bool
{
return $this->client->ping();
}
public function intervalCheck(): bool
{
return $this->client->ping();
}
public function objectRestore(): void
{
$this->client->reset();
}
public function gc(): void
{
$this->client->close();
}
}
业务代码通过 $connection->client() 取得客户端。这样连接的建立、探活、恢复和关闭规则都集中在一个类中。