php curl_multi_select 死循环原因及相应处理办法 | Summer Space, Summer Mind
现象
在系统更新以后,相应的php也升级到了新的版本.在运行了自己的代码之后,发现程序直接卡死不动了.在经过一阵排查之后,发现其卡在了非常诡异的地方
//use select to get response
//proceed select until all handle response
//can refer php.net page, curl_multi_select() api
while ($this->active && $mrc == CURLM_OK)
{
if (curl_multi_select($this->mh) != -1)
{
do {
$mrc = curl_multi_exec($this->mh, $this->active);
if ($mrc == CURLM_OK)
{
while($info = curl_multi_info_read($this->mh))
{
$this->process($info);
}
}
} while ($mrc == CURLM_CALL_MULTI_PERFORM);
}
}
另外可以注意到,CPU的使用率一直100%,说明我们的程序卡死了.
而经过一阵debug,发现程序运行到curl_multi_select之后,一直返回-1,然后就不断进入循环,卡死了.
原因探究
原来在10.8.5的时候,代码是能运行的,而且在linux中运行也很正常,因此,结果之后可能是php或者是相应依赖随着系统变化引起的问题.观察命名也能知道curl_multi_select其背后是基于libcurl进行实现的.而两个系统的libcurl版本也确实不同.
通过查看curl_multi_exec,有个用户的回复引起了注意.
Alex Palmer 10 months ago
On php 5.3.18+ be aware that curl_multi_select() may return -1 forever until you call curl_multi_exec(). See https://bugs.php.net/bug.php?id=63411 for more information.
查看bug列表,发现了原作者的回答:
[2012-11-03 03:42 UTC] pierrick@php.net
I’m not sure we really want to wait 1 second for nothing in this specific case. Furthermore, as mentioned in my commit message, when libcurl returns -1 in max_fd after calling curl_multi_fdset, it is because libcurl currently does something that isn’t possible for your application to monitor with a socket and unfortunately you can then not know exactly when the current action is completed using select().
I would personally keep the current behaviour.
所以显然是curl_multi_select
一直返回了-1,导致了程序进入了死循环,卡死掉了.而这个是进入了php5.3.8以后,做出的改变.
正确的curl_multi_select写法
那在这样的behaviour下,该如何编写curl_multi_select
呢.
经过一番研究,终于找到了合适的写法.关键在于在while循环中要执行curl_multi_exec
,直到处理完毕再进入select
.
实现代码:
while ($this->active && $mrc == CURLM_OK)
{
while (curl_multi_exec($this->mh, $this->active) === CURLM_CALL_MULTI_PERFORM);
if (curl_multi_select($this->mh) != -1)
{
do {
$mrc = curl_multi_exec($this->mh, $this->active);
if ($mrc == CURLM_OK)
{
while($info = curl_multi_info_read($this->mh))
{
$this->process($info);
}
}
} while ($mrc == CURLM_CALL_MULTI_PERFORM);
}
}
影响的php版本
这个我没有深入探究,理论上这个是和curl以及php版本有关系.
从其他使用者反馈的有:
- 5.4.7
- 5.4.8
- 5.3.17
- 5.4.7