2010-09-02 9 views
51

Estoy trabajando con una secuencia de comandos (que no creé originalmente) que genera un archivo pdf desde una página HTML. El problema es que ahora lleva mucho tiempo, como 1-2 minutos, procesar. Supuestamente esto funcionaba bien originalmente, pero se ha ralentizado en las últimas semanas.PHP file_get_contents muy lento al usar la url completa

El script llama al file_get_contents en un script php, que luego genera el resultado en un archivo HTML en el servidor y ejecuta la aplicación del generador de archivos PDF en ese archivo.

Parece que he reducido el problema a la llamada file_get_contents en una url completa, en lugar de una ruta local.

Cuando uso

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

se procesa de forma casi instantánea. Sin embargo, si utilizo la url completa

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

tarda de 30-90 segundos en procesarse.

No se limita a nuestro servidor, es lento cuando se accede a una url externa, como http://www.google.com. Creo que el script llama a la url completa porque hay variables de cadena de consulta que son necesarias y que no funcionan si llama al archivo localmente.

También probé fopen, readfile, y curl, y todos fueron similarmente lentos. ¿Alguna idea sobre dónde buscar para solucionar esto?

Respuesta

1

¿Puedes intentar recuperar esa url, en el servidor, desde la línea de comandos? curl o wget vienen a la mente. Si aquellos recuperan la URL a una velocidad normal, entonces no es un problema de red y probablemente algo en la configuración de apache/php.

+1

Cuando intento wget desde la línea de comandos, eso también es muy lento. Está colgando en el paso de resolución ... Algún tipo de problema de DNS en el servidor? – ecurbh

+0

Podría ser. Intente usar 'host' o 'nslookup' (lo que esté disponible) y trate de resolver varios nombres de host diferentes del sistema. –

26

Usaría curl() para buscar contenido externo, ya que es mucho más rápido que el método file_get_contents. No estoy seguro si esto resolverá el problema, pero vale la pena intentarlo.

También tenga en cuenta que la velocidad de sus servidores afectará el tiempo que lleva recuperar el archivo.

Aquí es un ejemplo de uso:

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

¿puede vincular a un punto de referencia sobre la comparación de file_get_contents y las velocidades de curvatura? – shamittomar

+0

@shamittomar, los puntos de referencia varían, pero un simple google puede generar un montón de resultados diferentes. http://stackoverflow.com/questions/555523/file-get-contents-vs-curl-what-has-better-performance es uno de ellos. Solo sé que cURL es más rápido de varias aplicaciones que he usado en el pasado. Eso es solo por experiencia personal y tiene sentido ya que cURL fue desarrollado por la única razón de obtener archivos remotos. Donde se desarrollaron file_get_contents/fopen para leer generalmente archivos locales. –

+0

Gracias por el enlace de la pregunta SO. – shamittomar

165

Nota: Esto se ha corregido en PHP 5.6.14. Un encabezado Connection: close ahora se enviará automáticamente incluso para las solicitudes HTTP/1.0. See commit 4b1dff6.

Tuve un momento difícil para averiguar la causa de la lentitud de los scripts file_get_contents.

Al analizarlo con Wireshark, el problema (en mi caso y probablemente el tuyo) era que el servidor web remoto NO CERRÓ LA CONEXIÓN TCP HASTA 15 SEGUNDOS (es decir, "keep-alive").

De hecho, file_get_contents no envía un encabezado HTTP de "conexión", por lo que el servidor web remoto considera de manera predeterminada que es una conexión keep-alive y no cierra la transmisión TCP hasta 15 segundos (Puede que no sea valor estándar - depende del servidor conf).

Un navegador normal consideraría que la página está completamente cargada si la longitud de la carga HTTP alcanza la longitud especificada en el encabezado HTTP Content-Length de la respuesta. File_get_contents no hace esto y es una pena.

SOLUCIÓN

lo tanto, si usted quiere saber la solución, aquí está:

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

La cosa es sólo para indicar al servidor Web remoto para cerrar la conexión cuando la descarga está completo, ya que file_get_contents no es lo suficientemente inteligente como para hacerlo solo con el encabezado HTTP Content-Length de respuesta.

+1

Gracias por esto, super dulce. – woodscreative

+20

Esta respuesta debe aceptarse ... – javsmo

+3

Aceptada, dorada, enmarcada y celebrada. Muchas gracias. – Mave

5

A veces, es debido a que el DNS es demasiado lento en su servidor, intente esto:

reemplazar

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

como

$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

Este fue el problema en mi caso. Se configuraron dos servidores DNS en '/ etc/resolv.conf', pero el primer servidor era inalcanzable. Las consultas de DNS agotaron el tiempo de espera en el primer servidor y luego saltaron al segundo servidor DNS varios segundos después. – thirdender

+0

O simplemente reemplazar '$ resultado = file_get_contents ('http://google.com', falsa, $ context);' con '$ ip = gethostbyname ('google.com');' ' $ result = file_get_contents ("http: // $ ip", false, $ context); ' –

2

que tenía el mismo problema,

Lo único que funcionó para mí es establecer timeo ut en $options matriz.

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

es el tiempo de espera pero no tengo idea de por qué. Mi mejor suposición es que hay una estupidez de IPv6 en OS X que no se puede apagar. Curl funciona bien, pero file_get_contents tomará más de 60 segundos dependiendo del tiempo de espera. NOTA: IPv6 está deshabilitado en la interfaz pública para este crapintosh, no puede deshabilitar IPv6 globalmente o en el bucle invertido. –

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

Tiempo: 50976 ms (avaerage tiempo total de 5 intentos)

$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); 

Tiempo: 46679 ms (avaerage tiempo total de 5 intentos)

Nota : request.php se utiliza para recuperar algunos datos de la base de datos mysql.

+0

¿Son estos datos de tiempo? Si es así deberías elaborar un poco más. –

0

Tengo una gran cantidad de datos pasados ​​por API, estoy usando file_get_contents para leer los datos, pero tardó alrededor de 60 segundos. Sin embargo, usando la solución de KrisWebDev tomó alrededor de 25 segundos.

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

Lo que también consideraría con Curl es que puede "enhebrar" las solicitudes. Esto me ha ayudado inmensamente ya que no tengo acceso a una versión de PHP que permita enhebrar en este momento.

Por ejemplo, obtenía 7 imágenes de un servidor remoto que utilizaba file_get_contents y tardaba entre 2 y 5 segundos por solicitud. Solo este proceso estaba agregando 30 segundos o algo al proceso, mientras el usuario esperaba que se generara el PDF.

Esto literalmente redujo el tiempo a aproximadamente 1 imagen. Otro ejemplo, verifico 36 URL en el tiempo que tomó antes para hacer una. Creo que entiendes el punto.:-)

$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); 

A continuación, compruebe lo largo de los resultados:

  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]); 

continuación, cierre de archivos:

curl_multi_close($master); 
0

sé que es cuestión de edad, pero me pareció que hoy y respuestas no lo hicieron trabaja para mi. No vi a nadie decir que las conexiones máximas por IP se pueden establecer en 1. De esa manera estás haciendo una solicitud de API y la API está haciendo otra solicitud porque utilizas la URL completa. Es por eso que cargar directamente desde el disco funciona. Para mí, eso solucionó un problema:

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);