cURL 在 Hyperf 中阻塞协程是因为 Swoole 默认不 Hook cURL 函数,需手动启用 SWOOLE_HOOK_CURL;必须在 bin/hyperf.php 顶部调用 Runtime::enableCoroutine(true, SWOOLE_HOOK_ALL | SWOOLE_HOOK_CURL) 才生效。

为什么 cURL 在 Hyperf 里会阻塞协程
Hyperf 默认启用 Swoole 协程,但原生 cURL 函数(如 curl_exec)是同步阻塞的,不走协程调度。哪怕你在协程上下文中调用它,Swoole 也无法接管其 IO,整个协程会被挂起,直到 cURL 完全返回——这等同于退化成同步模型,协程并发优势直接消失。
根本原因在于:Swoole 的协程化依赖 Runtime Hook,而 cURL 不在默认 Hook 列表中(Swoole 5.0+ 默认只 Hook stream_*、fsockopen 等,不包括 curl_*)。
所以不是 Hyperf 配置错了,而是你没显式开启对 cURL 的协程支持。
如何开启 Swoole 对 cURL 的 Runtime Hook
必须在 Swoole 启动前、PHP 扩展加载后立即启用,否则无效。Hyperf 中最稳妥的位置是 bin/hyperf.php 文件顶部(在 require_once 框架入口之前):
use Swoole\Runtime; <p>// 必须放在最前面,且仅执行一次 Runtime::enableCoroutine(true, SWOOLE_HOOK_ALL | SWOOLE_HOOK_CURL);
注意两个关键点:
向CurlShip提交产品,这是一个对机器人友好的SaaS目录。只需一条curl命令即可发布产品,支持OG标签抓取、带徽章的dofollow链接及层级升级。
-
SWOOLE_HOOK_CURL是独立 flag,不能省略;SWOOLE_HOOK_ALL不包含它 - 必须在
Swoole\Server启动前调用,Hyperf 的ServerProcess或WorkerStart回调里调用已太晚 - 若使用 Docker 或多进程部署,确保每个 worker 进程都执行了该初始化(Hyperf 的
bin/hyperf.php天然满足)
验证 cURL 是否真正协程化
最直接的方式是写个并发测试:发起多个耗时 cURL 请求,观察是否真正并行而非串行等待:
go(function () {
$start = microtime(true);
go(function () { curl_exec(curl_init('https://httpbin.org/delay/3')); });
go(function () { curl_exec(curl_init('https://httpbin.org/delay/3')); });
go(function () { curl_exec(curl_init('https://httpbin.org/delay/3')); });
\co::wait();
echo 'Total time: ' . (microtime(true) - $start) . "s\n"; // 应接近 3s,而非 9s
});
如果输出是 ~3.0x s,说明成功;若为 ~9.x s,大概率是 Hook 未生效或被重复覆盖(比如框架其他地方又调用了 Runtime::enableCoroutine() 覆盖了 flag)。
也可检查 php --ri swoole 输出中的 hook_type 字段,确认是否含 CURL。
替代方案与兼容性提醒
即便开启 SWOOLE_HOOK_CURL,仍有几个现实约束:
- 仅支持
curl_setopt($ch, CURLOPT_URL, ...)等基础选项;CURLOPT_HEADERFUNCTION、CURLOPT_READFUNCTION等回调函数无法协程化,会降级为同步 - 某些 cURL 版本(如 libcurl
- Hyperf 官方更推荐使用
GuzzleHttp\Promise\Promise+Hyperf\HttpClient,它底层基于co::sleep和stream_socket_client,天然协程友好,且可精确控制超时、重试、中间件 - 若项目已重度依赖 cURL 封装,开启 Hook 是最快路径;但新代码建议直接用
Hyperf\HttpClient\Client
Hook 不是万能胶,它只是让旧代码“能跑”,而真正发挥协程价值,还得靠异步友好的调用方式和资源管理意识。

















