PHP Multi-cURL 请求延迟到超时

PHP Multi-cURL requests delayed until timeout

总结

我有一些 PHP 5.4 代码可以使用 multi curl 并行获取一批 Facebook/Instagram 照片。这段代码已经运行多年,据我所知没有任何变化。

我将多个 curl 请求添加到一个 'multi' 请求中。每个 curl 请求都会得到一个 CURLOPT_TIMEOUT。我看到的问题是,突然之间,我的一些请求直到达到此超时才完成(无论我设置的超时是什么)。

代码

我做了这样的事情(简化):

do {
    while (CURLM_CALL_MULTI_PERFORM === curl_multi_exec($mh, $running));

    // Wait for activity on any curl-connection (optional, reduces CPU)
    curl_multi_select($mh);

    // a request was just completed -- find out which one
    while($done = curl_multi_info_read($mh))
    {
        $completedCurlRequest = $done['handle'];

        //save the file
        do_some_work(completedCurlRequest);

        curl_multi_remove_handle($mh, $completedCurlRequest);
    }
} while ($running);

我使用此脚本 运行 批量处理大约 40 个并行请求以获取一些图像(来自 Facebook)。他们中的大多数需要大约 500 毫秒才能完成。但是,一些请求 "hang"(直到 CURLOPT_TIMEOUT)才到达。

基本上 curl_multi_select 步骤占用了整个超时时间。或者,如果我删除那条 curl_multi_select 行,外循环将旋转(燃烧 CPU)直到超时。

注意事项

问题

  1. 我在使用 multi-curl 时是否做错了什么可能会导致这种情况? (但如果是这样,有什么变化?)
  2. Facebook 和 Instagram 是否更改了可能导致此问题的任何内容?
  3. 我服务器上的某些内容是否已更改以触发此操作?
  4. 我该如何调试它?

更新 这是我从一个缓慢的请求最终完成时得到的结果:

信息

"content_type": "image/jpeg",
"http_code": 200,
"header_size": 377,
"request_size": 180,
"total_time": 15.001012,    //<----- Total time == CURLOPT_TIMEOUT
"namelookup_time": 0.007149,
"connect_time": 0.12018,
"pretransfer_time": 0.441911,
"size_download": 40714,
"speed_download": 2714,
"download_content_length": -1,   //<------Not set

HEADER

HTTP/2 200 
content-type: image/jpeg
x-haystack-needlechecksum: 3529661797
timing-allow-origin: *
access-control-allow-origin: *
cache-control: max-age=1209600, no-transform
date: Mon, 04 Feb 2019 14:04:17 GMT
access-control-expose-headers: X-FB-CEC-Video-Limit

它缺少 content-length header,但在第一次获取文件时似乎总是如此。 50 个并行请求中只有 1 或 2 个很慢,但所有请求都缺少其内容长度 headers。

如果我再次获取同一个文件,速度会快得多,而且我确实看到这次设置了内容长度

信息

"download_content_length": 52721,

HEADER

content-length: 52721           

我目前的理论是 Facebook 文件服务器中存在一个错误,这意味着即使数据已发送,连接有时也不会关闭,因此连接会一直保持打开状态,直到超时。如果 Facebook 的文件服务器没有发送(可选)content-length header,cURL 无法知道负载是否完整,因此会挂起。

我目前的解决方案是 'prime' 文件服务器,方法是首先请求没有 body 的图像,如下所示:

$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_NOBODY, 1);
curl_exec($ch);

这是一个非常快速的过程,因为没有图像被返回。我实际上是在后台使用异步 multi curl 执行此操作的,因此我可以继续进行其他处理。

启动文件服务器后,后续对文件的请求甚至比以前更快,因为 content-length 是已知的。

这种方法有点笨拙,但由于到目前为止 Facebook 没有任何回应,我不确定还能做什么。