Запросы PHP Multi-cURL задерживаются до истечения времени ожидания - PullRequest
0 голосов
/ 01 февраля 2019

Сводка

У меня есть некоторый код PHP 5.4, который выбирает партию фотографий из Facebook / Instagram параллельно, используя multi curl.Этот код работал годами, и, насколько я могу судить, ничего не изменилось.

Я добавляю несколько запросов 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 мс.Однако некоторые запросы «зависают» (до CURLOPT_TIMEOUT) до их поступления.

В основном шаг curl_multi_select занимает весь тайм-аут.Или, если я удаляю эту строку curl_multi_select, внешний цикл вращается (сжигание ЦП) до истечения времени ожидания.

Соображения

  • Это неНеважно, что такое тайм-аут - если я установлю тайм-аут на 30 с, они прибудут через 30 секунд, если я установлю тайм-аут на 1 с, они прибудут через 1 с!

  • Этодействительно внезапное изменение, которое не связано ни с каким выпуском кода - все работало нормально до 30 января 2019 года, но 31-го внезапно перестало работать.

  • Это не так простовоспроизвести, так как это влияет только на изображение один раз.Если я повторю это для пакета изображений, который я уже получил, то в следующий раз он будет работать нормально.

  • Это влияет как на изображения на Facebook, так и на Instagram, поэтому я думаю, что проблема должна заключаться в том, чтобыс моим кодом или моим сервером (а не Facebook или Instagram), поскольку они оба не изменили бы что-то одновременно.

Вопросы

  1. Я что-то не так делаю при использовании multi-curl, что может вызвать это?(но если так, что изменилось?)
  2. Изменили ли Facebook и Instagram что-либо, что могло бы вызвать это?
  3. Возможно, что-то на моем сервере изменилось, чтобы вызвать это?
  4. Какя могу отладить это?

Обновление Вот что я получаю после медленного запроса, когда он наконец завершается:

INFO

"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, но это всегда имеет место при первой загрузке файла.Только 1 или 2 из 50 параллельных запросов являются медленными, но во всех запросах отсутствуют заголовки длины содержимого.

Если я получаю тот же файл снова, это происходит намного быстрее, и я вижу, что длина содержимогоустановите это время

INFO

"download_content_length": 52721,

HEADER

content-length: 52721           

1 Ответ

0 голосов
/ 04 февраля 2019

Моя текущая теория заключается в том, что в файловом сервере Facebook есть ошибка, которая означает, что соединение иногда не закрывается, даже если данные отправлены, поэтому соединение остается открытым до истечения времени ожидания.В отсутствие (необязательного) заголовка длины содержимого, отправляемого файловым сервером Facebook, cURL не может знать, завершена ли полезная нагрузка, и поэтому зависает.

Мое текущее решение - «заправить» файловый сервер с помощьюсначала сделать запрос на изображение без тела, например:

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

Это довольно быстрый процесс, так как изображение не возвращается.На самом деле я делаю это в фоновом режиме, используя асинхронный мулти-керл, поэтому я могу продолжать выполнять некоторые другие операции.

После заполнения файлового сервера последующие запросы к файлам выполняются даже быстрее, чем раньше, так как длина содержимогоизвестен.

Это немного неуклюжий подход, но из-за отсутствия какого-либо ответа от Facebook до сих пор я не уверен, что еще делать.

...