[PHP] Timeout bij meerdere aanroepen file_get_contents

Pagina: 1
Acties:

Vraag


Acties:
  • 0 Henk 'm!

  • Buur75
  • Registratie: December 2006
  • Laatst online: 10-04 15:21
Ik heb een rij met artikelnummers, en daarmee kan ik informatie opvragen (in XML) bij een andere website. Deze informatie sla ik op in mijn eigen database, maar er komen regelmatig nieuwe artikelen bij, en dat houdt in dat ik soms vele artikelen tegelijkertijd moet importeren. Ik doe dit met een file_get_contents in een loop, maar als het teveel artikelen zijn, geeft mijn script logischerwijze een timeout. Hoe voorkom ik dit?

Het makkelijkste is natuurlijk om de timeout van php aan te passen:
PHP:
1
ini_set('default_socket_timeout', 900);
maar dit wil ik niet. Het makkelijkste is denk ik een script dat de xml-file van 1 artikel ophaalt en verwerkt, en als dat klaar is, automatisch opnieuw opstart en dan het volgende artikel ophaalt, totdat ze allemaal geweest zijn. Ik kan dan ook iets van feedback op het scherm zetten met hoe ver hij al is (bijvoorbeeld: importeren: 3/120), zonder dat ik mij bezig hoef te houden met streaming-technieken.

De vraag is, hoe krijg ik dat voor elkaar? Of is er een betere manier voor?

Ik laat in het kort zien wat ik nu heb, en wat bij veel files een timeout geeft:
PHP:
1
2
3
4
5
6
7
8
9
$objectids = array(); // is een lijst met artikelnummers

foreach ($objectids as $artikelnr) {
 $url = "https://www.externeurl.com/xmlapi/article?id=". $artikelnr;
 $file = file_get_contents($url);
 $xml = simplexml_load_string($file);

 // vervolgens wordt de xml-file verwerkt en worden de gegevens in de eigen database gezet
}

Alle reacties


Acties:
  • 0 Henk 'm!

  • CyBeR
  • Registratie: September 2001
  • Niet online

CyBeR

💩

Krijg je een timeout op file_get_contents() of krijg je een php error omdat je de max execution time overschrijdt?

All my posts are provided as-is. They come with NO WARRANTY at all.


Acties:
  • 0 Henk 'm!

  • Buur75
  • Registratie: December 2006
  • Laatst online: 10-04 15:21
(bedankt voor de snelle reactie!)

De foutmelding die ik krijg:
Fatal error: Maximum execution time of 30 seconds exceeded in bestandsnaam on line 49
En op regel 49 staat $file = file_get_contents($url), dus ik vermoed het eerste.

Acties:
  • 0 Henk 'm!

  • orf
  • Registratie: Augustus 2005
  • Laatst online: 14:41

orf

Ik weet niet of het inmiddels veranderd is, maar file_get_contents op externe files was vroeger altijd extreem veel trager dan iets ophalen met Curl of met een socket.

Acties:
  • 0 Henk 'm!

  • CyBeR
  • Registratie: September 2001
  • Niet online

CyBeR

💩

Nee, dat is juist het tweede. Zie ook hoe er letterlijke "maximum execution time" staat, zoals ik ook al zei. Dat gezegd hebbende als die server op externeurl er lang over doet om te antwoorden kan 't zijn dat die dat veroorzaakt natuurlijk.

Het probleem is dus dat je er in totaal meer dan 30 seconden over doet om alles te downloaden en verwerken. Er zijn grofweg twee manieren om daar mee om te gaan, gegeven dat je verwerking zelf niet sneller kan: je kunt de maximale tijd oprekken (set_time_limit()) of de het aantal te downloaden en verwerken files limiteren en werken in batches.

[ Voor 12% gewijzigd door CyBeR op 28-11-2017 16:23 ]

All my posts are provided as-is. They come with NO WARRANTY at all.


Acties:
  • 0 Henk 'm!

  • Buur75
  • Registratie: December 2006
  • Laatst online: 10-04 15:21
Ja je hebt uiteraard gelijk, het is het tweede.

Dat de server waar de xml files vandaan komen niet sneller is, is niet erg, het maakt niet uit als mijn import-proces erg lang duurt, maar ik wil wel dat het helemaal automatisch gaat, dus dat ik niet zelf 20 keer op een knop moet drukken met tussenpauzen van 30 seconden... 8)7

Ik zou het in batches van 5 artikelen kunnen doen, maar hoe zorg ik er dan voor dat mijn script automatisch opnieuw wordt opgestart als hij klaar is met het verwerken, en de volgende 5 pakt? Ik zou denk ik wel iets met jQuery in elkaar kunnen zetten om dat opnieuw opstarten voor elkaar te krijgen, maar kan dat ook met alleen PHP?

Acties:
  • +1 Henk 'm!

  • orf
  • Registratie: Augustus 2005
  • Laatst online: 14:41

orf

Toch lijkt het me goed om even te benchmarken wat file_get_contents voor extra tijd kost.
Zelf doe ik dit soort dingen meestal door het uit elkaar te trekken. Het ene script zet alleen maar "jobs" klaar in een database-tabel; een cron op de achtergrond werkt openstaande jobs weg en updatet de tabel.

Als je wil kun je in je interface dan prima de voortgang weergeven. Een scriptje kan ophalen wat de voortgang is in de tabel. Dat scriptje kun je met ajax ophalen.

Tegenwoordig zijn queues misschien wat hipper.

Acties:
  • 0 Henk 'm!

  • mcDavid
  • Registratie: April 2008
  • Laatst online: 13:21
Het echte probleem is dat je voor een deel van je proces afhankelijk bent van externe systemen. Het maakt niet uit hoe hoog je de timeout zet, er gaat altijd een moment komen dat die 3e partij offline is en de boel crasht. Eigenlijk zou je zo'n proces in een workflow af willen handelen, zodat je het kunt hervatten als er iets misgegaan is.

Maar ik heb het idee dat dat misschien overkill is voor wat je wilt doen, dus misschien moet je eerst eens simpelweg je process forken met pcntl_fork() of zo.

Acties:
  • 0 Henk 'm!

  • DJMaze
  • Registratie: Juni 2002
  • Niet online
Buur75 schreef op dinsdag 28 november 2017 @ 16:33:
Dat de server waar de xml files vandaan komen niet sneller is, is niet erg, het maakt niet uit als mijn import-proces erg lang duurt, maar ik wil wel dat het helemaal automatisch gaat, dus dat ik niet zelf 20 keer op een knop moet drukken met tussenpauzen van 30 seconden... 8)7
Dan is je import vast klein. Ik moet bestanden van 5G importeren.
Buur75 schreef op dinsdag 28 november 2017 @ 16:33:
Ik zou het in batches van 5 artikelen kunnen doen, maar hoe zorg ik er dan voor dat mijn script automatisch opnieuw wordt opgestart als hij klaar is met het verwerken, en de volgende 5 pakt? Ik zou denk ik wel iets met jQuery in elkaar kunnen zetten om dat opnieuw opstarten voor elkaar te krijgen, maar kan dat ook met alleen PHP?
Gebruik set_time_limit i.c.m. stream functions of fsockopen i.p.v. file_get_contents()
Kan je alle kanten op, incl. TLS en passive FTP (PASV/EPSV) verbindingen en het downloaden van 5G bestanden.

Maak je niet druk, dat doet de compressor maar


Acties:
  • 0 Henk 'm!

  • Kontsnorretje
  • Registratie: Augustus 2011
  • Laatst online: 14-06-2024
Je kan het proces ook in 2en hakken.

Eerst heb je een service die de bestanden opvraagt en opslaat. Dit kan evt. in batches zijn, die je bijhoudt in een database.

Vervolgens heb je een service die de opgeslagen bestanden verwerkt. Als er geen bestanden zijn, stopt het script onmiddelijk.

Door een cronjob roep je de eerste service aan. Dit kan je hierdoor elke dag/week/maand laten uitvoeren.

Met een tweede cronjob, roep je de service aan die de bestanden verwerkt. Deze verwerkt een x aantal bestanden per batch. Op dat moment zet je een flag dat er data verwerkt wordt. Dit kan een .lock bestandje zijn in de map, of een veldje in een database. Aan het einde van de batch unset je die flag weer. Als de flag is geset, of als er geen bestanden zijn, stopt het script onmiddellijk. Hierdoor kan je dit script bijvoorbeeld elke paar minuten kunnen draaien.Beter is het natuurlijk om dit af te stemmen op het 1e proces.

Dit is uiteraard een (tamelijk eenvoudig te implementeren) voorbeeld, en er zijn tal van andere manieren om dit aan te pakken... Maar het komt er op neer dat wanneer je het op een dergelijke manier implementeert, je waarborgt dat je niet tegen een timeout aanloopt wanneer je de bestanden verwerkt (incidenten daargelaten). Onafhankelijk van het aantal bestanden dat je wil verwerken. Ik verwacht dan ook dat je timeout op andere locaties in het script optreed afhankelijk van de response time van de externe server.

Acties:
  • 0 Henk 'm!

  • thaan
  • Registratie: Oktober 2004
  • Laatst online: 14:59
Buur75 schreef op dinsdag 28 november 2017 @ 16:33:
Ja je hebt uiteraard gelijk, het is het tweede.

Dat de server waar de xml files vandaan komen niet sneller is, is niet erg, het maakt niet uit als mijn import-proces erg lang duurt, maar ik wil wel dat het helemaal automatisch gaat, dus dat ik niet zelf 20 keer op een knop moet drukken met tussenpauzen van 30 seconden... 8)7

Ik zou het in batches van 5 artikelen kunnen doen, maar hoe zorg ik er dan voor dat mijn script automatisch opnieuw wordt opgestart als hij klaar is met het verwerken, en de volgende 5 pakt? Ik zou denk ik wel iets met jQuery in elkaar kunnen zetten om dat opnieuw opstarten voor elkaar te krijgen, maar kan dat ook met alleen PHP?
Door vooraf te tellen hoeveel er zijn, een counter mee te laten lopen, na het doen van elk product +1 te doen in je counter, zodra de counter 5 raakt het script opnieuw starten (zodat je niet over de timeout heengaat) en de volgende 5 artikelen laten verwerken?

Dat kan prima met alleen PHP zonder externe libraries/jQuery/laravel.

Acties:
  • 0 Henk 'm!

  • pottink
  • Registratie: Augustus 2010
  • Laatst online: 25-06 10:32
Je kan natuurlijk ook altijd gebruik maken van een queue (denk aan RabbitMQ) als je heel veel artikelen moet gaan importeren en je geen timeouts wil tegenkomen in je gewone webapplicatie.

[ Voor 16% gewijzigd door pottink op 10-01-2018 13:06 ]


Acties:
  • 0 Henk 'm!

  • Harrie_
  • Registratie: Juli 2003
  • Niet online

Harrie_

⠀                  🔴 🔴 🔴 🔴 🔴

Buur75 schreef op dinsdag 28 november 2017 @ 15:58:
Het makkelijkste is denk ik een script dat de xml-file van 1 artikel ophaalt en verwerkt, en als dat klaar is, automatisch opnieuw opstart en dan het volgende artikel ophaalt, totdat ze allemaal geweest zijn. Ik kan dan ook iets van feedback op het scherm zetten met hoe ver hij al is (bijvoorbeeld: importeren: 3/120), zonder dat ik mij bezig hoef te houden met streaming-technieken.
Dan doe je toch een AJAX-call? Je hebt al een array met artikel-ID's, dus je kunt best op basis van die ID's in je pagina wat hidden elementen maken die het artikel-ID bevatten. Na page load loop je dan met JS/JQuery door die set elementen heen en doe je per ID een AJAX-call naar een ander PHP-bestand die dus enkel en alleen dat artikel verwerkt en de info terugpost. Visueel kun je dan op je scherm een counter laten lopen of per artikel melden dat dat specifieke artikel is geïmporteerd.

Hoeder van het Noord-Meierijse dialect


Acties:
  • 0 Henk 'm!

  • CurtPoindexter
  • Registratie: Februari 2017
  • Niet online
-

[ Voor 102% gewijzigd door CurtPoindexter op 19-10-2019 15:10 . Reden: Leeg ivm privacy ]


Acties:
  • 0 Henk 'm!

  • Harrie_
  • Registratie: Juli 2003
  • Niet online

Harrie_

⠀                  🔴 🔴 🔴 🔴 🔴

CurtPoindexter schreef op dinsdag 9 januari 2018 @ 09:11:
Wat wel een probleem is, is dat je met file_get_contents loopt te hanessen. Die functie is niet echt handig voor wat je nu wilt. Je kan er inderdaad bestanden mee downloaden van het internet maar verder heeft het veel nadelen:

• Krijg je een 404, een 500 of een andere melding? Geen idee!
• Krijg je wel een ander bestand? Geen XML? Zipje? Geen idee!
• Timeout? Redirect? Geen idee, of gewoon een dikke error.
Los van het feit dat dat TP wellicht beter kan gaan cURLen zie ik de relatie niet zo met de genoemde nadelen en file_get_contents. TP verwacht toch een XML met daarin bepaalde data om er vervolgens iets mee te doen? Welke methode je ook gebruikt, het lijkt me meer dan logisch dat je, voordat je data naar je DB gaat lopen pushen, eerst controleert of er in je XML (of wat voor bestand je dan ook geserveerd hebt gekregen) wel de benodigde tags (<PRODUCT>...</PRODUCT> bijv.) aanwezig zijn? Zijn die niet aanwezig dan kun je toch een generieke error posten dat de data niet juist of niet compleet is?

Hoeder van het Noord-Meierijse dialect

Pagina: 1