72 votes

PHP file_get_contents très lent lorsqu'il utilise l'url complète

Je travaille avec un script (que je n'ai pas créé à l'origine) qui génère un fichier pdf à partir d'une page HTML. Le problème est qu'il prend maintenant un temps très long, comme 1-2 minutes, à traiter. Il est supposé que cela fonctionnait bien à l'origine, mais que cela a ralenti au cours des deux dernières semaines.

Le script appelle file_get_contents sur un script php, qui sort ensuite le résultat dans un fichier HTML sur le serveur, et exécute l'application de génération de pdf sur ce fichier.

Je semble avoir réduit le problème au file_get_contents sur une url complète, plutôt que sur un chemin local.

Quand j'utilise

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

il traite presque instantanément. Cependant, si j'utilise l'url complète

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

le traitement prend entre 30 et 90 secondes.

Ce n'est pas limité à notre serveur, il est lent lors de l'accès à toute url externe, telle que http://www.google.com . Je crois que le script appelle l'url complet parce qu'il y a des variables de chaîne de requête qui sont nécessaires et qui ne fonctionnent pas si vous appelez le fichier localement.

J'ai aussi essayé fopen , readfile et curl et ils étaient tous aussi lents. Avez-vous des idées sur la façon de résoudre ce problème ?

184voto

KrisWebDev Points 661

Note : Cela a été corrigé dans PHP 5.6.14. A Connection: close sera désormais automatiquement envoyé même pour les requêtes HTTP/1.0. Voir l'engagement 4b1dff6 .

J'ai eu du mal à trouver la cause de la lenteur des scripts file_get_contents.

En l'analysant avec Wireshark, le problème (dans mon cas et probablement le vôtre aussi) était que le serveur web distant ne fermait pas la connexion TCP avant 15 SECONDES (c'est-à-dire "keep-alive").

En effet, file_get_contents n'envoie pas d'en-tête HTTP "connection", donc le serveur web distant considère par défaut qu'il s'agit d'une connexion keep-alive et ne ferme pas le flux TCP avant 15 secondes (Ce n'est peut-être pas une valeur standard - cela dépend de la configuration du serveur).

Un navigateur normal considère que la page est entièrement chargée si la longueur de la charge utile HTTP atteint la longueur spécifiée dans l'en-tête HTTP Content-Length de la réponse. File_get_contents ne le fait pas et c'est bien dommage.

SOLUTION

Donc, si vous voulez connaître la solution, la voici :

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

La chose est juste de indiquer au serveur web distant de fermer la connexion lorsque le téléchargement est terminé. car file_get_contents n'est pas assez intelligent pour le faire lui-même en utilisant l'en-tête HTTP Content-Length de la réponse.

1 votes

Merci pour ça, c'est très gentil.

3 votes

Accepté, doré, encadré et célébré. Merci beaucoup.

0 votes

Est-ce que cela peut être fait du côté de somepage.html ? (si somepage.html est un script php qui peut sortir des headers) J'ai essayé header('Connection : close') ; mais cela n'a pas marché.

40voto

Brad F Jacobs Points 12725

J'utiliserais curl() pour récupérer le contenu externe, car cette méthode est beaucoup plus rapide que l'option file_get_contents méthode. Je ne sais pas si cela résoudra le problème, mais ça vaut le coup d'essayer.

Notez également que la vitesse de vos serveurs aura une incidence sur le temps nécessaire à la récupération du fichier.

Voici un exemple d'utilisation :

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

0 votes

Pouvez-vous indiquer un lien vers un benchmark comparant les vitesses de file_get_contents et de curl ?

0 votes

@shamittomar, les benchmarks varient, mais une simple recherche sur google permet de trouver un tas de résultats différents. stackoverflow.com/questions/555523/ est l'un d'entre eux. Je sais simplement que cURL est plus rapide grâce à diverses applications que j'ai utilisées dans le passé. C'est donc juste une expérience personnelle et c'est logique car cURL a été développé pour la seule raison de récupérer des fichiers distants. Alors que file_get_contents / fopen ont été développés pour lire des fichiers locaux.

0 votes

L'un des avantages de curl est qu'il réutilisera une connexion existante (en utilisant le même identifiant), ce qui est important si vous effectuez plusieurs requêtes vers un même hôte (par exemple, des appels API).

8voto

diyism Points 1191

Parfois, c'est parce que le DNS est trop lent sur votre serveur, essayez ceci :

remplacer

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

comme

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

C'était le problème dans mon cas. Deux serveurs DNS ont été configurés dans /etc/resolv.conf mais le premier serveur était inaccessible. Les recherches DNS se sont arrêtées sur le premier serveur, puis sont passées au second serveur DNS plusieurs secondes plus tard.

0 votes

Ou simplement remplacer $result = file_get_contents('http://google.com', false, $context); avec $ip = gethostbyname('google.com'); $result = file_get_contents("http://$ip", false, $context);

3voto

Walid Ammar Points 2793

J'ai eu le même problème,

La seule chose qui a fonctionné pour moi est de définir un délai d'attente dans le fichier $options le tableau.

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

0 votes

C'est le délai d'attente mais je ne sais pas pourquoi. Ma meilleure hypothèse est qu'il y a une stupidité IPv6 sur OS X que vous ne pouvez pas désactiver. Curl fonctionne bien, mais file_get_contents prend plus de 60 secondes en fonction du délai d'attente. NOTE : IPv6 est désactivé sur l'interface publique de cet ordinateur de merde, vous ne pouvez pas désactiver IPv6 globalement ou sur le loopback.

3voto

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

C'est l'heure : 50976 ms (temps d'avaerage au total 5 essais)

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

C'est l'heure : 46679 ms (temps d'avaerage au total 5 essais)

Note : request.php est utilisé pour récupérer certaines données de la base de données mysql.

0 votes

S'agit-il de données de synchronisation ? Si oui, vous devriez élaborer un peu plus.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X