PHP file_get_contents очень медленный при использовании полного URL-адреса

Я работаю со сценарием (который я не создавал изначально), который создает файл PDF с HTML-страницы. Проблема в том, что сейчас требуется очень много времени, например, 1-2 минуты. Предположительно, это работало нормально, но замедлилось в течение последних нескольких недель.

Скрипт вызывает file_get_contents на php-скрипте, который затем выводит результат в HTML-файл на сервере и запускает приложение-генератор pdf в этом файле.

Кажется, я сузил проблему до вызова file_get_contents на полный URL, а не на локальный путь.

Когда я использую

 $content = file_get_contents('test.txt'); 

он обрабатывается почти мгновенно. Однако, если я использую полный URL-адрес

 $content = file_get_contents('http://example.com/test.txt'); 

для обработки требуется от 30 до 90 секунд.

Он не ограничивается нашим сервером, он медленен при доступе к любому внешнему URL-адресу, например http://www.google.com . Я считаю, что сценарий вызывает полный URL-адрес, потому что есть необходимые строковые переменные запроса, которые не работают, если вы вызываете файл локально.

Я также пробовал fopen , readfile и curl , и все они были так же медленны. Любые идеи о том, где искать, чтобы исправить это?

Примечание. Это было исправлено в PHP 5.6.14. Connection: close заголовок теперь будет автоматически отправляться даже для запросов HTTP / 1.0. См. 4b1dff6 .

Мне было трудно понять причину медленности скриптов file_get_contents.

Анализируя это с помощью Wireshark, проблема (в моем случае и, вероятно, ваша тоже) заключалась в том, что удаленный веб-сервер НЕ ЗАКРЫВАЕТ СОЕДИНЕНИЕ TCP до 15 SECONDS (т.е. «keep-alive»).

В самом деле, file_get_contents не отправляет HTTP-заголовок «соединение», поэтому по умолчанию удаленный веб-сервер считает, что это соединение keep-alive и не закрывает поток TCP до 15 секунд (это может быть не стандартное значение – зависит на сервере conf).

Обычный браузер считает, что страница полностью загружена, если длина полезной нагрузки HTTP достигает длины, указанной в ответе HTTP-заголовка Content-Length. File_get_contents не делает этого, и это позор.

РЕШЕНИЕ

Итак, если вы хотите знать решение, вот оно:

 $context = stream_context_create(array('http' => array('header'=>'Connection: close\r\n'))); file_get_contents("http://www.something.com/somepage.html",false,$context); 

Дело только в том, чтобы сообщить удаленному веб-серверу закрыть соединение при завершении загрузки , так как file_get_contents недостаточно интеллектуальны, чтобы сделать это самостоятельно, используя ответ HTTP-заголовка Content-Length.

Я бы использовал curl () для извлечения внешнего контента, так как это намного быстрее, чем метод file_get_contents . Не уверен, что это решит проблему, но стоит того.

Также обратите внимание, что скорость ваших серверов будет влиять на время, необходимое для получения файла.

Вот пример использования:

 $ch = curl_init(); curl_setopt($ch, CURLOPT_URL, 'http://example.com/test.txt'); curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); $output = curl_exec($ch); 

Иногда это происходит потому, что DNS слишком медленный на вашем сервере, попробуйте следующее:

замещать

 echo file_get_contents('http://www.google.com'); 

в виде

 $context=stream_context_create(array('http' => array('header'=>"Host: www.google.com\r\n"))); echo file_get_contents('http://74.125.71.103', false, $context); 

У меня была такая же проблема,

Единственное, что сработало для меня, – это установить тайм-аут в массиве $options .

 $options = array( 'http' => array( 'header' => implode($headers, "\r\n"), 'method' => 'POST', 'content' => '', 'timeout' => .5 ), ); 

Можете ли вы попробовать получить этот URL-адрес на сервере из командной строки? завиток или wget приходят на ум. Если они получают URL с нормальной скоростью, то это не сетевая проблема и, скорее всего, что-то в настройке apache / php.

 $context = stream_context_create(array('http' => array('header'=>'Connection: close\r\n'))); $string = file_get_contents("http://localhost/testcall/request.php",false,$context); 

Время: 50976 мс (время avaerage всего 5 попыток)

 $ch = curl_init(); $timeout = 5; curl_setopt($ch, CURLOPT_URL, "http://localhost/testcall/request.php"); curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, $timeout); echo $data = curl_exec($ch); curl_close($ch); 

Время: 46679 мс (время avaerage всего 5 попыток)

Примечание. Request.php используется для извлечения некоторых данных из базы данных mysql.

У меня огромные данные, переданные API, я использую file_get_contents для чтения данных, но это заняло около 60 секунд . Однако, используя решение KrisWebDev, потребовалось около 25 секунд .

 $context = stream_context_create(array('https' => array('header'=>'Connection: close\r\n'))); file_get_contents($url,false,$context); 

То, что я также рассмотрел бы с Curl, это то, что вы можете «направить» запросы. Это очень помогло мне, так как у меня нет доступа к версии PHP, которая позволяет нарезать резьбу на данный момент.

Например, я получал 7 изображений с удаленного сервера с помощью file_get_contents и занимал 2-5 секунд за запрос. Только этот процесс включал 30 секунд или что-то в этом процессе, в то время как пользователь ждал создания PDF-файла.

Это буквально сократило время до примерно 1 изображения. Другой пример: я проверяю 36 URL-адресов за время, которое потребовалось ранее, чтобы сделать это. Я думаю, вы понимаете. 🙂

  $timeout = 30; $retTxfr = 1; $user = ''; $pass = ''; $master = curl_multi_init(); $node_count = count($curlList); $keys = array("url"); for ($i = 0; $i < $node_count; $i++) { foreach ($keys as $key) { if (empty($curlList[$i][$key])) continue; $ch[$i][$key] = curl_init($curlList[$i][$key]); curl_setopt($ch[$i][$key], CURLOPT_TIMEOUT, $timeout); // -- timeout after X seconds curl_setopt($ch[$i][$key], CURLOPT_RETURNTRANSFER, $retTxfr); curl_setopt($ch[$i][$key], CURLOPT_HTTPAUTH, CURLAUTH_ANY); curl_setopt($ch[$i][$key], CURLOPT_USERPWD, "{$user}:{$pass}"); curl_setopt($ch[$i][$key], CURLOPT_RETURNTRANSFER, true); curl_multi_add_handle($master, $ch[$i][$key]); } } // -- get all requests at once, finish when done or timeout met -- do { curl_multi_exec($master, $running); } while ($running > 0); 

Затем проверьте результаты:

  if ((int)curl_getinfo($ch[$i][$key], CURLINFO_HTTP_CODE) > 399 || empty($results[$i][$key])) { unset($results[$i][$key]); } else { $results[$i]["options"] = $curlList[$i]["options"]; } curl_multi_remove_handle($master, $ch[$i][$key]); curl_close($ch[$i][$key]); не  if ((int)curl_getinfo($ch[$i][$key], CURLINFO_HTTP_CODE) > 399 || empty($results[$i][$key])) { unset($results[$i][$key]); } else { $results[$i]["options"] = $curlList[$i]["options"]; } curl_multi_remove_handle($master, $ch[$i][$key]); curl_close($ch[$i][$key]); 

затем закройте файл:

  curl_multi_close($master); 

Я знаю, что это старый вопрос, но я нашел его сегодня, и ответы не помогли мне. Я не видел, чтобы кто-то говорил, что максимальное количество соединений на IP может быть установлено равным 1. Таким образом, вы выполняете API-запрос, а API выполняет другой запрос, потому что вы используете полный URL-адрес. Вот почему загрузка непосредственно с диска работает. Для меня это устранило проблему:

 if (strpos($file->url, env('APP_URL')) === 0) { $url = substr($file->url, strlen(env('APP_URL'))); } else { $url = $file->url; } return file_get_contents($url);