PHP-Script aufrufen, ohne die Antwort abwarten zu müssen

mapcado

Grünschnabel
Hallo, liebe Tutorialisten!

Ich habe ein Problem, mit dem ich leider nicht fertig werde. Die Lösung dafür ist schon halb fertig, allerdings weiß ich nicht, ob es vielleicht einen weitaus einfacheren Ansatz gibt.

Aber nun zu meinem Problem: Und zwar nutze ich für ein Web-Projekt die eBay-Api. Diese limitiert die Anzahl der Aufrufe auf insgesamt 18 Requests gleichzeitig, allerdings benötige ich für jede Suche sehr viele Api-Calls (teilweise mehrere 100 gleichzeitig). Ich habe mir bereits eine Funktion gebaut, die diese Anzahl Requests mittels curl_multi relativ schnell abarbeiten kann, dabei aber immer höchstens 18 Calls gleichzeitig "am Laufen" hat.

Klingt soweit eigentlich ganz gut, aber was ist nun, wenn 2 (oder noch mehr) User gleichzeitig eine Suche durchführen? Dann gäbe es einen Zeitpunkt, in dem ich 36 oder mehr Calls gleichzeitig durchführen würde. Dies führt zum einen zu einer Verlangsamung der Gesamtmenge der Requests, und zum anderen bei zu häufigen "Missbrauch" zwangsläufig zum Ausschluss aus dem eBay Developers Program.

Daher habe ich mir folgende Lösung überlegt, und wüsste jetzt gern, ob das so weiter geführt werden kann, oder evtl. völliger Humbug ist: Ich schreibe die Daten zu den ganzen Calls einfach in eine SQL-Tabelle, und lasse sie von einem Script (im folgenden einfach makeCalls.php genannt) abarbeiten, welche die Ergebnisse dann in eine weitere Tabelle schreibt. Dieses Script darf allerdings immer nur einmal gleichzeitig ausgeführt werden, daher wird in einer weiteren Tabelle vermerkt, ob es schon läuft. Wenn ja, wird es garnicht erst aufgerufen. Nun habe ich allerdings das Problem, dass das aufrufende Script schließlich immer darauf warten muss, dass makeCalls.php fertig wird, und das ist Mist, denn das erste Script wird durch eine Ajax-Anfrage vom Client sufgerufen, und soll so schnell wie möglich Daten zurückliefern, die der Client dringend braucht. Dieser kontaktiert dann wiederum über Ajax einmal in der Sekunde ein weiteres Script, das den Status (einfach eine Zahl zwischen 0 und 100%) zurückgibt, wie groß der Anteil der Calls ist, die schon durchgeführt wurden. Der Client ruft dann bei 100% ein weiteres Script auf, welches die gewünschten Ergebnisse des Api-Calls ausgibt. Wenn nun aber gerade viel los ist auf der Website, kann es ewig dauern, bis makeCalls.php fertig ist, daher keine Statusabfrage und keine Daten. Gibt es daher irgendeine Möglichkeit in PHP, ein Script, das auf dem gleichen Server liegt, aufzurufen ohne die Antwort abwarten zu müssen? In der Doku über curl oder fsockopen konnte ich leider nichts finden.

Der Königsweg ist das allerdings nicht, daher poste ich hier auch mal meine anderen Lösungsansätze:
1. makeCalls.php als cronjob laufen lassen, allerdings halte ich es für Ressourcenverschwendung, alle 100ms eine Datenbankabfrage zu machen, die mit sehr großer Wahrscheinlichkeit ohnehin nichts hergibt.
2. Das ganze über den Client laufen lassen. Ich nutze das Script, welches makeCalls.php aufruft, sozusagen für einen REST-Webdienst, den der Client über Ajax in Anspruch nimmt. Der könnte erst das erste Script aufrufen. Wenn dieses dann fertig geladen hat, also die Tabelleneinträge komplett vorgenommen hat, ruft der Client makeCalls.php auf. Mit Ajax ist es ja ganz einfach, asynchrone Anfragen auszuführen, bei denen man die Antwort garnicht erst abwarten muss. Dies ist meiner Meinung nach die einfachste Lösung, hat aber den Nachteil, dass sehr viel vom Client abhängt, und auf diesen verlasse ich mich nur sehr ungern.

Und nun, zu guter letzt noch das, was ich mir eigentlich vorstellen würde, aber - wie bereits erwähnt - keine Ahnung habe, wie es umzusetzen sein könnte: Script_1.php ruft makeCalls.php auf, und gibt, ohne auf die Antwort zu warten. sofort die gewünschten Daten an den Client weiter. makeCalls.php sieht in der SQL-Tabelle nach, ob es schon läuft und macht sich, wenn nicht, an die Ausführung der Api-Calls. Durch den Cleint erfolgen dann die Statusabfrage, sowie der Download der gewünschten Ergebnisse des Api-Calls, aber dafür habe ich schon eine Lösung.

Aber natürlich ist dieser Lösungsansatz nicht in Stein gemeißelt. Wenn ihr bessere Vorschläge habt, nur her damit. Vielleicht ist die Lösung auch ganz einfach, nur seh ich den Wald vor lauter Bäumen nicht.

So, das wars. Ich hoffe, der Text war nicht zu lang oder zu kompliziert oder beides und mein Problem einigermaßen verständlich.

PS: Wie ihr an meinem Beitrag wahrscheinlich erkennen könnt, bin ich noch ein ziemlicher Anfänger auf dem Gebiet, also tut bei euren Antworten bitte so, als würdet ihr eurer Oma erklären, worum es geht.
 
OK, evtl. war der Text doch etwas zu lang. Für alle Lesefaulen :)-)) unter euch auf eine Zeile komprimiert: Ist es möglich, z.B. mit curl oder fsockopen eine .php-Datei aufzurufen, die auf dem gleichen Server liegt, die Antwort jedoch nicht abwarten zu müssen?

Pseudo-Code-mässig stelle ich mir das so vor:

PHP:
$ch = curl_init($_SERVER['DOCUMENT_ROOT']."/script.php?option=value");
curl_setopt($ch, CURLOPT_ANTWORT_NICHT_ABWARTEN_MUESSEN, 1);
curl_exec($ch);
exit; // und zwar bevor "script.php" fertig ist

Nur gibt es diese curl-Option nicht, habs schon ausprobiert ;-) Also, wie geht das? Weiss das einer?
 
Hallo nochmal!

Habe immer noch keine Lösung für das Problem gefunden, und bin langsam echt am verzweifeln. Kann mir einer von euch vielleicht einen Tipp geben, ob man das ganze in irgendeiner anderen serverseitigen Scriptsprache (perl, oder so) realisieren kann? Hat jemand von euch vielleicht mal ein ähnliches Problem gehabt? Wenn ja, wie habt ihr es gelöst?
 
Ohne jetzt komplett durchgelesen zu haben. Eine Warteschlange macht auch auf anderen Seiten Sinn. warum nicht diese aufbauen. Anhand der Position in der Wartschlange wird eine geschätzte Zeit eruiert, runtergezählt, dazu noch eine zB 12 Stunden gültige URL mit ID angezeigt.

mfg chmee
 
Hallo!

Erstmal vielen Dank für deine Antwort. Sowas wie eine Warteschlange realisiere ich auch, indem ich einfach alle URLs in eine SQL-Tabelle schreibe. Das Problem ist nur die Abarbeitung. Dazu brauche ich ein Script, welches garantiert höchstens einmal gleichzeitig läuft, damit nicht zu viele Requests gleichzeitig ausgeführt werden. Auch das ist eigentlich kein Problem, wenn dieses Script in einer weiteren Tabelle vermerkt, ob es schon läuft, und, wenn ja, sich sofort wieder beendet. Das Problem ist nur: Wenn ich dieses Script aufrufe, kann es sehr lange dauern, bis die Antwort kommt, wenn z.B. viel auf der Seite los ist, und ständig neue URLs in die Tabelle geschrieben werden, noch bevor alle abgearbeitet sind. Daher möchte ich gern ein Script aufrufen, ohne die Antwort abwarten zu müssen.

Oder meinst du mit "Warteschlange" vllt irgendeine PHP-Funktion, -Bibliothek, oder -Klasse, die ich nicht kenne? Konnte jetzt durch 5min Google auf die Schnelle nichts finden.
 
Die Warteschlange wird einfach anders aufgebaut. PRIMÄR wird die Abfrage nur durch die Einträge in der Warteschlange/Queue/Pipe (wie man es auch nennen will) bedient. Eine Anfrage von außen (über eine Seite) landet immer in der Queue.. Somit garantierst Du, dass die Anfragen wirklich nur sequentiell und nicht gleichzeitig ausgeführt werden. Zudem kann das im Hintergrund passieren, indem Du zB über _GET oder _POST-Variablen das Script sich selbst rekursiv aufruft - und wenn die Queue leer ist, stoppt. Somit verhinderst Du auch TimeOuts aufgrund langer Scriptzeiten..

Beim nächsten Aufruf der Seite - wenn also wiedermals eine Anfrage in die Queue geworfen wird, wird auch das Abfragescript wieder angestoßen.

mfg chmee
 
Hallo!

Danke für die Ausführungen. Nur damit ich das richtig verstehe: Du meinst, das Script soll erst die Abfrage durchführen, und sich dann selbst mit der nächsten URL im queue wieder aufrufen. Aber dann muss das Script - bevor es sich immer wieder selbst aufruft - doch erstmal von einem anderen Script aufgerufen werden. Und das muss dann so lange warten, bis alle Rekursionen durchlaufen sind, die queue also leer ist, genau das will ich aber vermeiden. Es soll einfach nur eine kleine Notiz bekommen, dass es loslegen soll, wenn es noch nicht dabei ist. Wenn es fertig ist, kann ich das an einem weiteren Datenbankeintrag erkennen, und der Client weiß, dass er mit dem Download der Daten beginnen kann.

Oder hab ich da irgendwas falsch verstanden?

PS: Ich hab mal nen bisschen gegooglet zum Thema PHP queue, aber nur Lösungen gefunden, bei denen die Erledigung absolut nicht eilt (Mail-Notifications, oder Aktualisierung von RSS-Feeds) und die daher als cronjob (ungefähr 1 x / Minute) laufen. Das geht bei mir aber nicht, weil ich meine Seitenbesucher schlecht eine Minute auf ihr Suchergebnis warten lassen kann. Daher müsste ich den Cronjob min. 1 x / Sekunde ausführen, aber ist das nicht Ressourcenverschwendung?
 
Hallo,

ich könnte mir hier ne reihe unterschiedlicher ansätze vorstellen. Je nachdem wie du das ganze umsetzen willst, und was du technisch (server) für möglichkeiten hast.

1.) queue system
Du könntest die abfragen in ein queue system wie beispielsweise php-resque oder rabbitmq pushen und assynchron verarbeiten lassen.
Erfordert auf der serverseite allerdings einen entsprechenden queueserver.

2.) den request vorzeitig beenden
Solltest du zu den leuten gehören die gerne innovative lösungen ausprobieren und sich nicht im einem standart apache begnügen, kann ich dir php-fpm nur nahelegen.
Damit - und nur damit - gibt es ne funktion die sich fastcgi_finish_request() nennt.
Wird sie aufgerufen, wird die browser anfrage beendet, der PHP prozess aber weiter ausgeführt.

3.) exec/proc_open/whatever
du kannst ein eigenes script asyncron starten und damit deine requests ausführen.
Achtung - verursacht einiges an last

4.) batch processing
schreib die requests in ne datenbank und verwende einen zyklischen cronjob um sie zu verarbeiten.

Das sind die optionen die mir spontan einfallen um sowas umzusetzen.

Ich persönlich würde warscheinlich php-resque verwenden. Einziger nachteil dabei ist halt, dass du einen server brauchst, wo du redis und den php-reque worker laufen lassen kannst.
Der grosse vorteil ist alledings, dass du ohne probleme über die anzahl worker steuern kannst wie viele requests gleichzeitig verarbeitet werden können. (eben z.B. genau 18)

edit: hier die referenz auf github: https://github.com/chrisboulton/php-resque
zusätzlich dazu brauchst du einen REDIS server http://redis.io/
 
Zuletzt bearbeitet:
Vielen Dank! Werd mir die Möglichkeiten mal ansehen und dann berichten, wofür ich mich entschieden habe. Nr. 1) und 2) werden wohl flach fallen, da ich nen Managed Server habe, bzw. ich werd mal anfragen, ob man sowas installieren könnte.

Danke nochmal und viele Grüße!
 
Um nochmal auf das Essentielle einzugehen.. Die eBay-API-Restriktionen, die Du nicht überlisten willst, setzen ganz klare Regeln. Der API ist es egal, ob Du einen Browserzugriff abbrichst, dort werden die API-Zugriffe gezählt, ob Du das nun asynchron oder sequentiell machst, ist denen soweit egal, als dass Du einfach eine bestimmte Anzahl nicht überschreiten darfst. Ich sehe da keine Möglichkeit, diese Einschränkungen zu überlisten - außer Du hast mehrere API-IDs. Wenn Du "nur" zB 1000 API-Zugriffe die Stunde machen darfst, kann auch anderer Code nix dran ändern.

Zu den Ideen von chibisuke: Die klingen doch alle recht ok. Ein managed Server wird genau in solchen Dingen ge'manage'd - damit man dem kompetenten "Manager" auch n paar Aufgaben überlassen kann. Oder Du hast nur nen Webhost.. Dann wäre - wenn meine Aussage in #6 verstanden wäre - die Wahl. Du baust eine Queue, die automatisiert abarbeitet, bis sie leer ist und erst beim nächsten Aufuruf und Füllen der Queue wieder anspringt. Ähnliches hatte ich schon Hier angesprochen, ein php-script, dass sich solange selbst aufruft, bis die Queue leer ist.

mfg chmee
 
Zurück