在使用curl_exec() 时,PHP脚本不会超时

后端开发 2026-07-10

我有一个通过launchd定期执行的PHP shell脚本。该脚本有一个2 分钟的PHP超时,但我理解curl_exec() 花费的时间不计入其中。不过我也设置了一个10秒的curl_exec() 超时,但在某些情况下也不会生效。

情况是:这个服务器有时会被DDoS攻击击垮,需要重置自己的TCP栈。在此期间,当这个脚本运行时,通常会在curl_exec() 函数上卡住很长时间,大约6 分钟左右才会失败。

其他人建议,我也同意,最有可能的情况是curl的内部部分在尝试发送HTTP请求时挂起,这些挂起要么不计入超时,要么只是某种漏洞导致整个函数在尝试发出这些HTTP请求时基本冻结。

以下是我的代码:

set_time_limit(120);

$ch = curl_init($url);
curl_setopt($ch,CURLOPT_USERAGENT,"Mozilla/5.0 etc");
curl_setopt($ch,CURLOPT_RETURNTRANSFER,true);
curl_setopt($ch,CURLOPT_CONNECTTIMEOUT,10);
curl_setopt($ch,CURLOPT_TIMEOUT,10);
curl_setopt($ch,CURLOPT_INTERFACE,OPERATING_IP);
$return = curl_exec($ch);

据我所知,这里的CURLOPT_CONNECTTIMEOUT超时被包含在CURLOPT_TIMEOUT超时中,因此调用它有点多余。但是在服务器进行TCP重置时,这个curl调用经常需要5 分钟或更长时间才能完成。

是否有其他方式可以让这次调用快速失败。它只是加载一个非常小的静态文件,10秒已经绰绰有余。

我唯一能想到的办法,是把curl_exec() 的包装函数单独放在一个脚本里,让我的脚本通过命令行参数调用它并传入实际的URL。然后再用一个CLI超时命令强制它在10秒后失败。但这是一种混乱的解法,肯定有比这更优雅的办法吧?

更新:我不想把这个作为正式的“答案”,因为它仍然是权宜之计。不过正如我在其中一个评论中提到的,我把curl请求改成了外部curl命令,并用timeout命令让它在10秒后失败。

$clicurl = "curl -A 'Mozilla/5.0 (etc)' -m 10 --interface ".OPERATING_IP." $url";
$check_result = exec("/usr/local/bin/timeout 10s $clicurl");

-curle -m 10是 curl内部的10秒超时,但在exec() 调用中的 timeout 命令强制了一个10秒超时,这在我的TCP栈被重置且无法解析时似乎确实起作用。请注意,唯一的问题是,当这个脚本被 launchd 运行时,它需要对 timeout 的完整路径,这与我在终端手动运行时不同。这让调试有点有趣……。所以这是我的权宜之计。如果有人知道在PHP中保持它并让它工作的方法,仍然更值得采用。

解决方案

这不是最优雅的解决方案,但它应该能改善较长的挂起时间。

// This will kill the request if it has been slower than 1 byte/sec for 3 seconds
curl_setopt($ch, CURLOPT_LOW_SPEED_LIMIT, 1);
curl_setopt($ch, CURLOPT_LOW_SPEED_TIME, 3);

文档

这些选项将让curl终止连接,这样就不会在陈旧的响应上继续等待。

站内所有文章版权归属LeftHeroAI导航站,无授权禁止任何主体转载、抄袭、复制内容,亦不得私自架设镜像站点。一经侵权,本站将通过法律途径追责。

相关文章