2010-09-02 2 views
51

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

Сценарий вызывает 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, и все они были так же медленными. Любые идеи о том, где искать, чтобы исправить это?

ответ

1

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

+1

Когда я пытаюсь выполнить wget из командной строки, это также очень медленно. Он висит на разрешающем шаге. Какая-то проблема с DNS на сервере? – ecurbh

+0

Может быть. Попробуйте использовать «хост» или «nslookup» (независимо от того, что доступно) и попытайтесь разрешить различные разные имена хостов из системы. –

26

Я бы использовал 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); 
+0

можете ли вы сослаться на сравнительный тест по сравнению файлов_и__по_оценок и скоростей скручивания? – shamittomar

+0

@shamittomar, тесты различаются, но простой Google может придумать кучу разных результатов. http://stackoverflow.com/questions/555523/file-get-contents-vs-curl-what-has-better-performance является одним из них. Я просто знаю, что cURL быстрее из разных приложений, которые я использовал в прошлом. Так что это только из личного опыта, и это имеет смысл, поскольку cURL был разработан только по причине получения удаленных файлов. Где, как file_get_contents/fopen, были разработаны для общего чтения локальных файлов. –

+0

Спасибо за ссылку на вопрос. – shamittomar

165

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

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

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

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

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

РЕШЕНИЕ

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

$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.

+1

Спасибо за это, супер сладкое. – woodscreative

+20

Этот ответ должен быть принят ... – javsmo

+3

Принято, позолочено, обрамлено и отмечено. Спасибо. – Mave

5

Иногда это происходит потому, что 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); 
+0

Это было проблемой в моем случае. Два DNS-сервера были настроены в '/ etc/resolv.conf', но первый сервер был недоступен. DNS-запросы, синхронизированные на первом сервере, затем несколько секунд спустя перепрыгнули на второй DNS-сервер. – thirdender

+0

Или просто заменить '$ Result = file_get_contents ('http://google.com', ложь, $ контекстные);' с '$ ф = gethostbyname ('google.com');' ' $ result = file_get_contents ("http: // $ ip", false, $ context); ' –

2

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

Единственное, что сработало для меня - установка timeo ut в $options массив.

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

- это время, но я понятия не имею, почему. Мое лучшее предположение - это глупость IPv6 в OS X, которую вы не можете отключить. Curl отлично работает, но file_get_contents займет более 60 секунд в зависимости от таймаута. ПРИМЕЧАНИЕ. IPv6 отключен в общедоступном интерфейсе для этого crapintosh, вы не можете отключить IPv6 в глобальном масштабе или в loopback. –

0
$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.

+0

Являются ли данные синхронизации? Если так, то вам нужно еще немного разобраться. –

0

У меня есть огромные данные, переданные 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); 
0

То, что я также рассматривал бы с помощью 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]); 

затем закрыть файл:

curl_multi_close($master); 
0

Я знаю, что это старый вопрос, но я нашел его сегодня, и ответы не работа для меня. Я не видел, чтобы кто-то говорил, что максимальное количество соединений на 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);