Multithreading ist in PHP eine echte Herausforderung. Das liegt aber im wesentlichen daran, dass PHP Code nicht wie in einem Java Container immer geladen ist, sondern erst bei Bedarf in den Speicher geladen wird, der Code ausgeführt wird und PHP dann wieder beendet wird.
PHP ist eine Skriptsprache, bei der so etwas also eigentlich nicht nötig sein sollte. Aber in meinem speziellen Anwendungsfall möchte ich möglichst viele URLs in kurzer Zeit aufrufen. Um Zeit zu sparen, macht es Sinn, die URLs nicht nacheinander, sondern gleichzeitig, abzurufen. Was für ein Glück, dass CURL in PHP verfügbar ist und die Bibliothek dies von Hause aus unterstützt.
Zum Testen benötige ich erst mal ein einfaches PHP Skript, das mir einen Zeitstempel ausgibt und etwas wartet, bevor es sich selbst beendet. Diese einfache Aufgabe übernimmt das folgende kleine Skript.
<?php // curl.php $sleep = 5; if (isset ($_GET['sleep'])) { $sleep = $_GET['sleep']; } echo "Time: " . date("d.m.Y H:i:s") . " Sleep:" . $sleep; sleep ($sleep); ?>
Dieses kleine Test Script hilft mir, dass folgende PHP Skript mit dem ich „PHP Multithreading mit cURL“ teste zu prüfen. Es macht eigentlich nicht viel. Es wird einfach nur die aktuelle Uhrzeit auf dem Server ausgegeben. Nach dem sleep wird das Skript dann beendet. Bei einer sequenziellen Verarbeitung würde jedes mal ein anderer Timestamp ausgegeben und die sleep Zeiten würden sich für die Ausführung addieren.
Mein PHP Multithreading mit cURL Skript sieht dann wie folgt aus:
<?php
// run.php // cURL Session Array $curl_array = array(); // Abzurufende URLs in einem Array ablegen $urls_array = array( 'http://test.1br.de/curl.php?sleep=1', 'http://test.1br.de/curl.php?sleep=2', 'http://test.1br.de/curl.php?sleep=3', 'http://test.1br.de/curl.php?sleep=4', 'http://test.1br.de/curl.php?sleep=5', 'http://test.1br.de/curl.php?sleep=6' ); // cURL Multi Handle erstellen und hinzufügen der Sessions $curl_multi_handle = curl_multi_init(); for($i = 0; $i < count($urls_array); $i++) { $url = trim ($urls_array[$i]); // cURL Session erstellen $curl_array[$i] = curl_init($url); // cURL Settings für die Session curl_setopt($curl_array[$i], CURLOPT_RETURNTRANSFER, true); //cURL handle zum cURL Multi Handle hinzufügen curl_multi_add_handle($curl_multi_handle, $curl_array[$i]); } //URLs abrufen (parallel innerhalb der cURL Bibliothek) do { curl_multi_exec($curl_multi_handle,$running); } while($running > 0); echo "Ausgabe der Seiteninhalte:\r\n"; for($i = 0; $i < count($urls_array); $i++) { $content = curl_multi_getcontent ( $curl_array[$i] ); echo $content . "\r\n"; } ?>
Das Skript ruft den URL http://test.1br.de/curl.php 6 mal mit unterschiedlichen sleep Parametern auf und gibt den HTTP Response aus. Der Test zeigt deutlich, dass alle 6 URLs gleichzeitig aufgerufen wurden. Da das curl.php Skript ein sleep nach der Ausgabe enthält, ist das schon Beweis genug, dafür, dass die Webseiten nicht nacheinander abgerufen werden sondern gleichzeitig.
# ./run.php Ausgabe der Seiteninhalte: Time: 01.07.2014 21:48:30 Sleep:1 Time: 01.07.2014 21:48:30 Sleep:2 Time: 01.07.2014 21:48:30 Sleep:3 Time: 01.07.2014 21:48:30 Sleep:4 Time: 01.07.2014 21:48:30 Sleep:5 Time: 01.07.2014 21:48:30 Sleep:6
Auch die Logfiles bestätigen, dass der Aufruf gleichzeitig erfolgt ist.
# tail -f test.1br.de-access_log xxx.xxx.xxx.x - - [01/Jul/2014:21:48:30 +0200] "GET /curl.php?sleep=1 HTTP/1.1" 200 33 xxx.xxx.xxx.x - - [01/Jul/2014:21:48:30 +0200] "GET /curl.php?sleep=2 HTTP/1.1" 200 33 xxx.xxx.xxx.x - - [01/Jul/2014:21:48:30 +0200] "GET /curl.php?sleep=3 HTTP/1.1" 200 33 xxx.xxx.xxx.x - - [01/Jul/2014:21:48:30 +0200] "GET /curl.php?sleep=4 HTTP/1.1" 200 33 xxx.xxx.xxx.x - - [01/Jul/2014:21:48:30 +0200] "GET /curl.php?sleep=5 HTTP/1.1" 200 33 xxx.xxx.xxx.x - - [01/Jul/2014:21:48:30 +0200] "GET /curl.php?sleep=6 HTTP/1.1" 200 33
Damit ist der Test der Funktion abgeschlossen und ich kann das cURL Multithreading in mein kleines aber feines Test Skript einfügen und nun noch mehr Webseiten gleichzeitig überprüfen.