使用curl_multi_*单请求为何仍有15-20次循环迭代?
为什么 curl_multi_select 不能阻塞到请求完成?
先明确文档里的核心逻辑:
该函数会阻塞脚本执行,直到关联的cURL句柄能在下次curl_multi_exec()调用时取得进展或超时(以先到者为准)。
这里的关键是**「取得进展」**,不是「请求完全结束」。一个HTTP请求从发起到完成要走多个步骤:DNS解析、TCP建连、发送请求头、接收响应头、分块接收响应体、关闭连接……每完成一个小步骤,curl就会标记“有进展可处理”,此时curl_multi_select就会结束阻塞,让你调用curl_multi_exec去处理这个阶段的状态。它不会等整个请求彻底做完才唤醒,而是每有阶段性成果就通知一次。
为什么你的代码会跑15次迭代?
你的循环逻辑是:每次先调用curl_multi_exec处理当前所有可处理的状态,要是还有未完成的请求,就用curl_multi_select等着下一次进展,接着处理已经完成的请求,直到没有未完成句柄为止。
那15次迭代,对应就是这个请求从开始到结束的15个阶段性节点:比如DNS解析完唤醒一次,TCP连好唤醒一次,收到一段响应体又唤醒一次……每触发一次唤醒,就会进入一轮循环处理状态,直到整个响应接收完毕,请求标记为完成,最后一轮循环处理完就退出了。
注释掉 curl_multi_select 为啥会有十几万次迭代?
去掉curl_multi_select后,脚本就进入了空转循环:没有任何阻塞,会反复调用curl_multi_exec,但绝大多数时候网络操作还在进行中,根本没有新状态可处理,所以循环会疯狂跑,直到请求完成,这就是次数爆炸的原因。而curl_multi_select的核心作用就是让脚本在没进展的时候休眠,别瞎浪费CPU,只有当有新状态需要处理时才唤醒,所以能把迭代次数压下来。
另外你说缩短timeout会增加迭代次数,这很好理解:超时时间短了,就算没新进展,select也会定期唤醒触发循环,迭代次数自然就变多了。
内容的提问来源于stack exchange,提问作者Mike

