Wie traditionelles PHP (FPM) funktioniert

FPM-Modell

Wenn eine PHP-Serveranwendung ein Restaurant waere, wuerde sie wahrscheinlich als ein Elite-Etablissement gelten, in dem jeder Tisch von einem eigenen Kellner bedient wird.

Jede neue Anfrage an den Server wird von einer separaten PHP-VM, einem Prozess oder Thread bearbeitet, danach wird der Zustand zerstoert. Das ist so, als wuerde ein Kellner einen Tisch bedienen und dann entlassen oder sein Gedaechtnis geloescht.

Dieses Modell hat einen Vorteil: Wenn ein PHP-Fehler auftritt, ein Speicherleck, eine vergessene Datenbankverbindung – es beeinflusst andere Anfragen nicht. Jede Anfrage ist isoliert. Das bedeutet, die Entwicklung ist einfacher, das Debugging ist einfacher und es gibt eine hohe Fehlertoleranz.

In den letzten Jahren hat die PHP-Community versucht, ein zustandsbehaftetes Modell einzufuehren, bei dem eine einzelne PHP-VM mehrere Anfragen bedienen kann und den Zustand zwischen ihnen bewahrt. Zum Beispiel erreicht das Laravel Octane-Projekt, das Swoole oder RoadRunner verwendet, eine bessere Leistung, indem es den Zustand zwischen Anfragen bewahrt. Aber das ist bei weitem nicht die Grenze des Moeglichen.

Einen Kellner nach jeder Bestellung zu entlassen ist zu teuer. Da Gerichte langsam in der Kueche zubereitet werden, verbringt der Kellner die meiste Zeit mit Warten. Das Gleiche passiert mit PHP-FPM: Die PHP-VM sitzt untaetig herum. Es gibt mehr Kontextwechsel, mehr Overhead fuer das Erstellen und Zerstoeren von Prozessen oder Threads, und mehr Ressourcenverbrauch.

// Traditionelles PHP-FPM
$user = file_get_contents('https://api/user/123');     // stehen und warten 300ms
$orders = $db->query('SELECT * FROM orders');          // stehen und warten 150ms
$balance = file_get_contents('https://api/balance');   // stehen und warten 200ms

// Verbraucht: 650ms reines Warten
// CPU ist untaetig. Speicher ist untaetig. Alles wartet.

Nebenlaeufigkeit

Nebenlaeufigkeitsmodell

Da die Kueche Gerichte nicht sofort zubereiten kann und der Kellner Leerlaufzeiten zwischen den Zubereitungen hat, gibt es die Moeglichkeit, Bestellungen von mehreren Kunden zu bearbeiten.

Dieses Schema kann ziemlich flexibel funktionieren: Tisch 1 bestellte drei Gerichte. Tisch 2 bestellte zwei Gerichte. Der Kellner bringt das erste Gericht zu Tisch 1, dann das erste Gericht zu Tisch 2. Oder vielleicht hat er es geschafft, zwei Gerichte zum ersten Tisch und eines zum zweiten zu bringen. Oder umgekehrt!

Das ist Nebenlaeufigkeit: Die gemeinsame Nutzung einer einzelnen Ressource (CPU) zwischen verschiedenen logischen Ausfuehrungsstroemen, die Koroutinen genannt werden.

use function Async\spawn;
use function Async\await;

// Alle drei Anfragen "nebenlaeufig" starten
$userTask = spawn(file_get_contents(...), 'https://api/user/123');
$ordersTask = spawn($db->query(...), 'SELECT * FROM orders');
$balanceTask = spawn(file_get_contents(...), 'https://api/balance');

// Waehrend eine Anfrage auf eine Antwort wartet, erledigen wir andere!
$user = await($userTask);
$orders = await($ordersTask);
$balance = await($balanceTask);

// Verbraucht: 300ms (die Zeit der langsamsten Anfrage)

Nebenlaeufigkeit ist nicht Parallelitaet

Es ist wichtig, den Unterschied zu verstehen.

Nebenlaeufigkeit – wie in True Async, JavaScript, Python:

Parallelitaet – das ist Multithreading (Go):

Was kommt als Naechstes?

Jetzt verstehen Sie das Wesentliche. Sie koennen tiefer eintauchen: