Problème de surnombre de fichiers temporaires

Petit nouveau ! | 7 Messages

06 juil. 2012, 14:40

Salut à tous. Je suis nouveau sur ce forum.

Normalement j'aime beaucoup me débrouiller tout seul pour résoudre mes problèmes, mais là je sèche complet.
Je vous explique :
Je dois importer dans une DB une grande quantité de données depuis différents fichiers XML distants (serveurs HTTP). Jusque là, pas de soucis. Comme 1 des fichiers est très volumineux (700Mo au moment ou je vous parle), j'ai codé mon parseur maison à l'aide de Sax.
Dans le principe, ça se passe comme ça : Le gros XML contient des références vers d'autres fichiers XML plus petits. Je parse donc le gros XML et pour chaque référence à un petit XML, je le télécharge et le parse à son tour pour extraire les infos. Le problème est ici : lorsque PHP va chercher un fichier distant, il créé un fichier temporaire dans lequel il copie le contenu du fichier distant. Mais quelle à été ma surprise, lorsque j'ai eu une erreur me disant "Too many opened files". Le problème vient du fait que PHP ne ferme pas ces fichiers temporaires (ou pas avant la fin du script plus exactement).
J'ai essayé de changer de technique, et d'utiliser cURL, sans succès, celui-ci fonctionnant exactement pareil.

Je vous met ici la portion de code consernée. Je ne met pas toute la classe du parseur, le fichier faisant presque 10Ko.

Code : Tout sélectionner

function __construct($xmlURL, $xmlGroupTagName, $xmlGroupTagGap, $useCurl = FALSE) { $this -> xmlURL = $xmlURL; $this -> xmlGroupTagName = $xmlGroupTagName; $this -> xmlGroupTagGap = $xmlGroupTagGap; $this -> saxParser = xml_parser_create('UTF-8'); xml_set_object($this -> saxParser, $this); xml_parser_set_option($this -> saxParser, XML_OPTION_CASE_FOLDING, FALSE); xml_parser_set_option($this -> saxParser, XML_OPTION_TARGET_ENCODING, 'UTF-8'); xml_set_character_data_handler($this -> saxParser, '__dataHandler'); xml_set_element_handler($this -> saxParser, '__startElement', '__endElement'); xml_set_default_handler($this -> saxParser, '__default'); if($useCurl) { $this -> xmlFileSocket = tmpfile(); $curlSession = curl_init($xmlURL); curl_setopt($curlSession, CURLOPT_FILE, $this -> xmlFileSocket); curl_setopt($curlSession, CURLOPT_ENCODING, 1); curl_exec($curlSession); if(curl_errno($curlSession)) { echo 'On attend 5min'; sleep(300); curl_exec($curlSession); if(curl_errno($curlSession)) return FALSE; } curl_close($curlSession); rewind($this -> xmlFileSocket); }
Cette fonction n'est bien sur pas achevée, et des "hack" tel que $useCurl et autres n'auront plus leusr place par la suite.

Pour moi, le rêve serai de trouver un moyen de charger les fichiers XML directement dans un fichier temporaire que je créé moi (et donc que je peux fermer sur demande).
Je précise que le serveur http est un Apache 2 et que la version de PHP est la 5.4.4 le tout sur une Arch Linux :p à jour

J'espère vraiment qu'une âme charitable saura me dépatouiller de tout ça car je suis en train de cracker. Le reste du projet n'avance pas du coup, et je prend du retard.
Merci à tous ceux essairons

Petit nouveau ! | 7 Messages

06 juil. 2012, 15:12

Pourquoi ne pas utiliser uniqid() et gérer toi même l'écriture dans tes propres fichiers temporaire au lieu d'utiliser tmpfile() ?

Petit nouveau ! | 7 Messages

06 juil. 2012, 15:41

Merci pour ta réponse :)

Je ne connaissais pas cette fonction, mais dans mon cas, elle ne m'aiderai pas.
Je ne me suis peut être pas bien exprimé : mon problème n'est pas le fichier temporaire que je créé moi via tmpfile(). Cette fonction me retourne un descripteur de fichier et je peut donc le fermer (et donc le supprimer) quand je veux. Mon problème c'est que lorsque j'ouvre le XML distant (que ce soit via fopen ou curl ou même je pense file_get_content ou autre), PHP, pour son fonctionnement interne, créé un fichier temporaire à lui (je ne connais pas son descripteur de fichier), qu'il ne ferme qu'a la fin du script. Comme je dois accéder à un grand nombre de fichiers XML distants, la quantité de fichiers temporaires de PHP explose jusqu’à la limite du système (sudo lsof -p pid_de_apache | wc -l est monté jusqu'a 5000 environ, ce qui semble être ma limite actuelle).

Ce qu'il me faudrait donc, c'est un moyen de récupérer les xml distants sans que PHP ne créé de fichier temporaire dans mon dos, ou à défaut, de fermer ces fichiers en question.

ViPHP
ViPHP | 2577 Messages

06 juil. 2012, 15:54

Edit : réponse sans intéret donc je supprime.

autre proposition :
Tu peux éventuellement faire une récupération des fichiers :
1) récupération du fichier mettre
2) constitution d'une liste des fichiers à récupérer (en session)
3) boucle avec redirect pour le chargement des fichiers (pas temporaire)
4) traitement des fichiers
Modifié en dernier par Mazarini le 06 juil. 2012, 16:09, modifié 1 fois.

Petit nouveau ! | 7 Messages

06 juil. 2012, 15:55

A en effet, je n'avais pas compris ça comme ça.
Pour ma part je ne connaissais pas tmpfile() (donc petit tour sur la doc php) et ne voyant pas de fclose() dans le code, j'ai pensé que tu gardais les fichiers temporaire pour un traitement ultérieur. Et vue que fclose() sur un tmpfile() supprime le fichier, j'ai pensé que tu parlais de ces fichiers.

Pour rebondir sur ce que dis Mazarini, quand tu as le cas des 5000 fichiers, c'est lorsque tu traites 5000 fichiers XML ?

Petit nouveau ! | 7 Messages

06 juil. 2012, 16:07

Merci à vous.

Alors oui il faut faire des fclose(), que je fais d'ailleur, mais dans le desctructeur :

Code : Tout sélectionner

function __destruct() { fclose($this -> xmlFileSocket); xml_parser_free($this -> saxParser); }
En effet le fichier temporaire que je créé contient le code XML que je dois parser. Il doit donc rester ouvert jusqu'à la fin du traitement, après quoi je le ferme et détruit le parseur.
Les fichiers temporaires qui me posent problèmes sont ceux que je ne créé pas moi même. De ce que j'ai compris, quand je demande à PHP de télécharger le fichier XML dans un fichier temporaire créé via tmpfile(), en effet, lui, télécharge le fichier dans un fichier temporaire à lui, puis recopie le tout dans mon fichier temporaire. Au final celà fait 2 fichiers. Je ferme bien le mien, mais je n'ai aucun contrôle sur l'autre (qui malheureusement ne se ferme pas en même temps).
Pour la cas des 5000 fichier (c'est pas un nombre exacte hein, j'ai dis ça un peu au pif mais cela semble être dans ces eaux là), oui, il y a 1 fichier temporaire pour un fichier XML. Je peux d'ailleur aller les voir dans /tmp, ils s'appellent tous php******** (* correspond à un caractère alpha numerique).
Du coup, j'avais penser à les supprimer à l'arache, mais en fait ça ne changerai rien vu que Linux n'efface pas vraiement les fichiers encore ouverts.
Bref, un gros bordel

Petit nouveau ! | 7 Messages

06 juil. 2012, 16:25

autre proposition :
Tu peux éventuellement faire une récupération des fichiers :
1) récupération du fichier mettre
2) constitution d'une liste des fichiers à récupérer (en session)
3) boucle avec redirect pour le chargement des fichiers (pas temporaire)
4) traitement des fichiers
Je ne comprend pas bien les points 1 et 3. Je ne comprend pas non plus ce que ça va changer, je devrai toujours ouvrir les fichiers et PHP crééra toujours ces fichiers qu'il ne fermera pas.
Est-ce que tu pourrai développer un petit peu s'il te plait ?

En tout cas, merci pour vos réponses :D

Petit nouveau ! | 7 Messages

06 juil. 2012, 17:00

Je viens de me rendre compte que PHP ne supprimes même pas les fichiers temporaires qu'il créés :?
Je suis de plus en plus en train de me dire que c'est un bug.

Je ne suis pas le seul on dirai qui soit confronté à ce problème, mais personne ne l'a réglé.

ViPHP
ViPHP | 2577 Messages

06 juil. 2012, 21:22

1) récupération du fichier maitre (oops) le gros fichier qui te permet de connaitre les autres fichiers
2) constitution d'une liste des fichiers à récupérer (en session)
3) boucle avec redirect pour le chargement des fichiers (pas temporaire)
4) traitement des fichiers

Pour le 1) faute d'orthographe.
Pour le 3, tu récupère un fichier et tu fais un redirect pour récupérer le suivant donc avec un autre lancement de php.

Mammouth du PHP | 2278 Messages

06 juil. 2012, 21:31

Je vous met ici la portion de code consernée. Je ne met pas toute la classe du parseur, le fichier faisant presque 10Ko.
Moi, je ne suis pas concerné, mais consterné
Vanitas vanitatum et omnia vanitas
Mes derniers livres :
Sauvez les Mots chez BoD,
Tous les chemins mènent à ROM chez BoD

Petit nouveau ! | 7 Messages

09 juil. 2012, 12:03

Désolé de ne pas avoir répondu plus tôt, j'étais pas dispo ce Week End.
1) récupération du fichier maitre (oops) le gros fichier qui te permet de connaitre les autres fichiers
2) constitution d'une liste des fichiers à récupérer (en session)
3) boucle avec redirect pour le chargement des fichiers (pas temporaire)
4) traitement des fichiers

Pour le 1) faute d'orthographe.
Pour le 3, tu récupère un fichier et tu fais un redirect pour récupérer le suivant donc avec un autre lancement de php.
Pour le point 2, je ne suis pas sur que celà soit une bonne idée de le faire comme ça. Je m'explique : le gros fichier est vraiment très gros, donc il faudra beaucoup de temps et de ressources pour le parser en entier et la liste contenant toutes les URL des petits fichier risque d'être monstrueuse en terme de place mémoire. Cette opération doit être limité en consommation de ressources car le site doit garder une disponibilité correcte pendant ce temps.
En revanche, le point 3 est très intéressant. Si j'ai bien compris, ce que tu propose est de créé un script indépendant pour télécharger les petits XML. Ainsi, on force PHP à fermer les fichiers temporaires puisque le script s'arrête.

Merci pour vôtre aide, je vais essayer ça et je vous tiens au courant.

ViPHP
ViPHP | 2577 Messages

09 juil. 2012, 13:44

Il doit être possible de faire un wget du fichier via l'exécution d'une commande systeme pour récupérer les fichiers sans passer par php.

Mammouth du PHP | 2278 Messages

09 juil. 2012, 14:09

Peut-on identifier où se trouvent ces fichiers qui dérangent ? (oui j'ai une arrière-pensée...)
Vanitas vanitatum et omnia vanitas
Mes derniers livres :
Sauvez les Mots chez BoD,
Tous les chemins mènent à ROM chez BoD

Petit nouveau ! | 7 Messages

09 juil. 2012, 15:47

Il doit être possible de faire un wget du fichier via l'exécution d'une commande systeme pour récupérer les fichiers sans passer par php.
Ba je ne crois pas y avoir accès sur le serveur final du site (hébergement mutualisé), mais je dois pourvoir me renseigner ^^
Peut-on identifier où se trouvent ces fichiers qui dérangent ? (oui j'ai une arrière-pensée...)
Oui, je sais ou ils sont exactement (/tmp/php*), et je crois savoir comment le faire savoir à PHP. J'y avais pensé (si tu pense à les supprimer), mais sous Linux, ça n'aurai aucune incidence. PHP pourrai continuer à travailler avec. Ils ne serai en fait supprimés qu'a la fin du script (donc on reviens à la case départ).

Là j'ai pas trop le temps de bricoler tout ça, mais dès que je l'ai fais, je vous le fais savoir. En tout cas merci de vôtre aide, ça va surement beaucoup m'aider :D