PHP 8.5 curl_share_init_persistent:跨请求复用 cURL 共享句柄
PHP 8.5 新增了一个持久化的 cURL 共享句柄构造函数:
curl_share_init_persistent(array $share_options): CurlSharePersistentHandle
它和 curl_share_init() 的差别在于生命周期:创建的句柄不会在 PHP 请求结束时销毁。如果内部已存在具有相同 share_options 的持久共享句柄,就直接复用该句柄。这一行为适合长生命周期 SAPI。
参数与错误
参数是一个非空的 CURL_LOCK_DATA_* 常量数组。数组里不能包含 CURL_LOCK_DATA_COOKIE,否则抛出 ValueError——请求之间共享 cookies 可能导致用户间的敏感 cookie 混淆。
会触发的错误/异常:
share_options为空 →ValueError- 含不匹配
CURL_LOCK_DATA_*的值 →ValueError - 含
CURL_LOCK_DATA_COOKIE→ValueError - 含非整数值 →
TypeError
用法
$sh = curl_share_init_persistent([CURL_LOCK_DATA_DNS, CURL_LOCK_DATA_CONNECT]);
$ch1 = curl_init("http://example.com/");
curl_setopt($ch1, CURLOPT_SHARE, $sh);
curl_exec($ch1); // 可能复用之前 SAPI 请求的连接
$ch2 = curl_init("http://example.com/");
curl_setopt($ch2, CURLOPT_SHARE, $sh);
curl_exec($ch2); // 复用 $ch1 的连接
实现要点
函数用 share_options 在内部构造持久 ID,用户不需要自己挑 persistent_id。每组唯一的 share_options 只对应一个共享句柄,因此连接数上限要自己在 CurlHandle 上设合适的 CURLOPT_MAXCONNECTS。
返回的 CurlSharePersistentHandle 与 CurlShareHandle 内部相同,可以用于 curl_setopt,但不能用于 curl_share_setopt,以保证句柄跨请求不可变。
相等性上有个容易踩的点:
curl_share_init_persistent([CURL_LOCK_DATA_DNS]) === curl_share_init_persistent([CURL_LOCK_DATA_DNS]) // false
因为每次调用都会重建对象,只是字段相同,比较应该用 == 而不是 ===。
性能收益
除 DNS 和连接外,还可以用 CURL_LOCK_DATA_SSL_SESSION 共享 TLS 会话,从而减少 DNS 解析与 SSL 握手——这两项通常占网络耗时的 30%–70%。
RFC 见 curl_share_persistence_improvement,实现见 php-src PR #16937。
Guzzle 的支持现状
guzzle/guzzle#3307 讨论把 curl_share_init_persistent 接进 CurlHandler。维护者指出 multi-handle 场景尚未实现,而 multi-handle 恰恰是 Guzzle 最有用的部分。作者的测试表明,easy handle 设置了 CURLOPT_SHARE 之后,curl_multi_add_handle 不会覆盖该 share handle,仍会复用连接(connect_time_us=0)。
Guzzle 的计划是分两步:
- 7.11 提供 handler 级共享,硬编码 DNS 与 SSL session;
- 8.0 提供持久共享(DNS、connection、SSL session),分
prefer(回退到 7.11 风格)与require(确保完整的持久共享)两种模式。
陷阱
持久句柄的生命周期跨请求,因此其适用前提是长生命周期 SAPI。短生命周期或进程模型下,请求结束后的资源回收语义与普通 curl_share_init() 不同,CURLOPT_MAXCONNECTS 之类的连接上限也需要按进程实际并发量显式设置。
链接
- PHP 手册:curl_share_init_persistent
- 示例文章:Use persistent cURL share handle in PHP 8.5
- RFC:
- 实现:
- Guzzle issue: