Parallelisierung in PHP, Beispiel einer Metasuchmaschine
Auch wenn es auf den ersten Blick nicht ganz offensichtlich ist, kann man in PHP Prozesse parallelisieren.
Möglich macht dies die PHP-Funktion “popen“, die einen neuen Prozesszeiger öffnet.
Anwendungsbereiche für die Parallelisierung
Weshalb kann es aber sinnvoll sein PHP mehrere Dinge gleichzeitig erledigen zu lassen, da ja nicht zusätzliche Prozessor-Resourcen und damit höhere Geschwindigkeit zur Verfügung stehen?
Auf vielen modernen Rechnern stehen inzwischen Mehrfachkerne zur Verfügung. Da ein einzelner Prozess gleichzeitig nur einem Kern ausgeführt werden kann, wird somit die volle Leistung des Systems nicht ausgereizt. Das Betriebssystem muss also in der Lage sein die Prozesse auf die verschiedenen Kerne zu verteilen. Wenn ein laufender PHP-Prozess einen weiteren startet, dann kann dieser auf einem anderen Kern laufen, was eine echte parallele Abarbeitung zur Folge hat.
Die größten Leistungszuwächse können aber immer dann entstehen, wenn die eigentliche Arbeit nicht auf dem Host ablaufen soll. Beispiele dafür sind Webservices oder auch simple HTTP-Anfragen (auch wenn man diese via “curl” anders parallelisieren kann). Voraussetzung dafür ist, dass es immer mehrere Funktionen oder Abfragen auf möglichst verschiedenen Hosts gibt. Außerdem dürfen die Funktionen oder Abfragen nicht nicht voneinander abhängig sein.
Ein Beispiel für eine solche Aufgabenstellung kann eine Meta-Suchmaschine sein. Also eine diesem Fall ein Skript, dass Suchergebnisse von verschiedenen Suchmaschinen gleichzeitig abfragt und das Ergebnis zusammenfasst.
Vorraussetzungen für die Parallelisierung in PHP
Auf dem Server auf dem das Skript ausgeführt werden soll muss eine “CLI” - PHP - Version installiert haben.
Die Rechte müssen so gesetzt sein, dass sowohl PHP - CLI als auch das Skript selbst von PHP ausgeführt werden kann.
Der Abfragende Quellcode und der Initiator müssen in unterschiedlichen Skripten liegen.
Beispielimplementation einer Mini Meta-Suchmachine
Das Skript, dass die Parallelisierung vornimmt öffnet mittels der “popen” Funktion so viele Prozesszeiger zum “Arbeitsskript” wie simultane Prozesse ablaufen sollen. Danach werden die Ergebnisse dieser Skript ausgelesen und weiterverarbeitet. Sollte ein Prozess länger dauern als die anderen, so wird beim Auslesen so lange gewartet, bis er fertig ist. Da alle anderen Prozesse dann bereits fertig sind, werden nur noch die Ergebnisse ausgelesen, so dass der Gesamtprozess effektiv nur so lange dauert wie die langsamste Anfrage.
Als Ergebnis liegt nun Array mit den URL’s der Suchergebnisse gruppiert nach Suchmaschinen vor, das entsprechend weiterverarbeitet werden kann.
<? $searchkey = 'guxx'; // Suchwort $pathtocli = '/usr/bin/php'; // Pfad zu CLI Version von PHP $pathtoscript = "/pfad/zum/skript/"; // Pfad zum auszuführenden Skript // Array mit den URL's der Suchabfragen $engines = array('http://www.google.de/search?hl=de&btnG=Google-Suche&meta=&q=', 'http://de.search.yahoo.com/search?fr=yfp-t-501&ei=UTF-8&rd=r1&p=', 'http://search.live.com/results.aspx?go=Suche&mkt=de-de&scope=&FORM=LIVSOP&q='); // Für jede URL einen Prozess zum Zielskript öffnen foreach ($engines as $key => $engine) { $fh[$key] = popen("$pathtocli $pathtoscript \"$engine$searchkey\"", 'r' ); } // Ergebnisse auslesen foreach ($fh as $key => $handle) { $result = ""; while (!feof($handle)) { $result .= fgets($handle); } $rar[$key] = explode("\n", $result); } print_r($rar); ?>
Das “Arbeisskript” ist für die eigentliche Anfrage an die Suchmaschinen zuständig und übernimmt in diesem Fall auch die Auswertung der Ergebnisse.
<? // Abfrage mit dem CLI Argument ausführen $result = @file_get_contents($argv[1]); $pattern[] = "/<h2\ class=r><a\ href=\"(http:\/\/.*?)\"\ class=l/"; // Pattern für Google Ergebnisse $pattern[] = "/class=yschttl href=\"(.*?)\"/"; // Pattern für Yahoo Ergebnisse $pattern[] = "/<h3><a\ href=\"(.*?)\"\ gping=/"; // Pattern für die Live-Suche // die Pattern auf das Ergebnis anwenden und "Newline" getrennt ausgeben foreach ($pattern as $pkey => $spattern) { if (preg_match_all($spattern, $result, $matches)) { if (count($matches[1]) > 0) { foreach($matches[1] as $key => $url) { echo("$url\n"); } } } } ?>
Verwandte Artikel
- Guxxviz Beispiel 1
- MathML kurz vorgestellt
- Gesichtserkennung in PHP
- Guxxviz
- SQL-Injektion - Einblicke und Gegenmaßnahmen
Beitrag: 19. September 2007 (11:25 Uhr) von juniperus
Kategorien: Server, Linux, PHP, Programmierung, Allgemein
Tags: php, suchmaschinen, webservices
Permanentlink: Parallelisierung in PHP, Beispiel einer Metasuchmaschine
Kommentarfeed abonnieren
Beitrag Kommentieren
TrackBack-URL







8 Kommentare zu “Parallelisierung in PHP, Beispiel einer Metasuchmaschine”
Wie ist denn das dann mit der foreach-Schleife? Wird da nicht wieder jeder Prozess nacheinander ausgelesen und wenn sagen wir mal der erste sehr lange braucht, muss unser Hauptskript doch trotzdem auf die Abarbeitung der jeweiligen Prozesse warten, oder nicht?
Hallo Christoph,
Die Frage ist berechtigt, aber:
dadurch, dass die Prozesse wirklich gleichzeitig ablaufen und die Ausgaben gepuffert werden spielt das warten keine Rolle.
Folgendes Szenario:
Es sollen 3 Jobs ausgeführt werden.
Der erste dauert 4 Sekunden. Der zweite 3 und der dritte 1 Sekunde.
Die Prozesse werden gestartet und laufen damit ab. Die Ausgaben werden gepuffert.
Die Schleife fängt beim ersten 4 Sekunden dauernden Prozess an, die Ausgaben aufzufangen. Das dauert natürlich 4 Sekunden. In der Zwischenzeit sind die anderen Prozesse fertig. Wenn die Schleife nun weiter abgearbeitet wird, dann werden nur noch die Ergebnisse aus dem Puffer gelesen, was zeittechnisch quasi vernachlässigbar ist. Also hat die gesamte Ausführung ca. 4 Sekunden gedauert.
Auch wenn ein anderer Job langsamer ausgeführt wird, so ergibt sich immer das gleiche Bild. Das ganze dauert maximal so lange wie der langsamste Job.
Du kannst das Skipt ja mal testen, indem Du als Job verschiedene Sleep-Zeiten ausführen lässt!
Das funktioniert genau nach Plan…
Kann denn PHP die Ausgaben bereits während der Abarbeitung der Prozesse auslesen? Also wenn der ersten 10 Sekunden lang etwas ausgibt, dass PHP bereits während dieser Zeit die Ergebnisse aus dem Puffer zieht? Das würde zwar an der Gesamtzeit nix ändern, aber viell. braucht man ja aus dem Gesamtprozess nur die ersten 3 Zeilen…
Oder muss jeder Prozesss erst abgeschlossen sein, bevor er ausgelesen werden kann?
[…] (more…) […]
Nach meinem Verständnis ist dieses Verfahren nur wirksam, wenn PHP im CGI bin läuft.
Wenn PHP als Apache Modul läuft, dann laufen ohnehin mehrere Prozesse parallel.
Aber ich werde mal versuchen, das in beiden Versionen zu testen.
coole idee. thx
Hier liegt ein Denkfehler vor. popen öffnet zwar einen neuen Prozess, aber dieser Vorgang läuft blockiert ab.
D.h. der nächste Befehl nach popen wird erst ausgeführt, wenn popen fertig ist.
Daher wird im oben gezeigten Beispiel nichts parallelisiert.
Ich habe auch noch kein Beispiel gesehen, wie Parallelisierung durch Threading oder mehrere Prozesse in PHP funktionieren soll. Was ginge ist der Aufruf weiterer Scripte mit fopen(url) und nachfolgendes Abschalten des stream blocking. Die Frage ist, inwiefern einem das noch nützt.
Christoph
[…] der vielen Behauptungen “Multithreading” sei nicht möglich, habe ich eine Lösung von Guxx.de modifiziert und mich durch vorwiegend englische und französische Blogs gelesen. Die Idee ist, eine […]