PHP ist allgegenwärtig und ist wohl die Sprache, die am häufigsten im Internet eingesetzt wird.
Allerdings ist sie nicht gerade für ihre Hochleistungsfähigkeiten bekannt, insbesondere wenn es um hochgradig nebenläufige Systeme geht. Und das ist der Grund, warum Sprachen wie Node (ja, ich weiß, es ist keine Sprache), Weiter und Elixier für solche speziellen Anwendungsfälle immer mehr an Bedeutung gewinnen
Dennoch gibt es eine Menge, was Sie tun können, um die PHP-Leistung auf Ihrem Server zu verbessern. Dieser Artikel konzentriert sich auf die php-fpm
Seite, die Sie natürlich auf Ihrem Server konfigurieren müssen, wenn Sie Nginx verwenden
Falls Sie wissen, was php-fpm
ist, können Sie gerne zu dem Abschnitt über die Optimierung springen
Was ist PHP-fpm?
Nicht viele Entwickler interessieren sich für die DevOps-Seite der Dinge, und selbst von denen, die sich dafür interessieren, wissen nur wenige, was unter der Haube vor sich geht. Interessanterweise ist es nicht PHP, das den ersten Kontakt herstellt, wenn der Browser eine Anfrage an einen Server sendet, auf dem PHP läuft, sondern der HTTP-Server, von denen die wichtigsten Apache und Nginx sind. Diese "Webserver" müssen dann entscheiden, wie sie sich mit PHP verbinden und den Anfragetyp, die Daten und die Kopfzeilen an PHP weiterleiten.

In modernen PHP-Anwendungen ist der obige Teil "Datei finden" die index.php
, die der Server so konfiguriert hat, dass alle Anfragen an sie weitergeleitet werden
Wie der Webserver die Verbindung zu PHP herstellt, hat sich weiterentwickelt, und dieser Artikel würde in seiner Länge explodieren, wenn wir auf alle Einzelheiten eingehen würden. Aber grob gesagt war PHP in der Zeit, in der Apache der Webserver der Wahl war, ein in den Server integriertes Modul
Wenn also eine Anfrage eintraf, startete der Server einen neuen Prozess, der automatisch PHP einschloss und es ausführen ließ. Diese Methode wurde mod_php
genannt, kurz für "PHP as a module" Dieser Ansatz hatte seine Grenzen, die Nginx mit php-fpm
hat überwunden
Bei php-fpm
liegt die Verantwortung für die Verwaltung der PHP-Prozesse bei dem PHP-Programm auf dem Server. Mit anderen Worten, dem Webserver (in unserem Fall Nginx) ist es egal, wo sich PHP befindet und wie es geladen wird, solange er weiß, wie er Daten senden und von ihm empfangen kann. Wenn Sie möchten, können Sie sich PHP in diesem Fall als einen weiteren Server vorstellen, der einige untergeordnete PHP-Prozesse für eingehende Anfragen verwaltet (die Anfrage erreicht auch einen Server, wird von einem Server empfangen und an einen Server weitergeleitet - ziemlich verrückt! :-P)
Wenn Sie schon einmal Nginx eingerichtet haben oder auch nur ein wenig darin herumgestöbert haben, werden Sie auf etwas wie dieses stoßen
Standort ~ .php$ { try_files $uri =404; fastcgi_split_path_info ^(. .php)(/. )$; fastcgi_pass unix:/run/php/php7.2-fpm.sock; fastcgi_index index.php; include fastcgi_params; fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; }
Die Zeile, die uns interessiert, ist diese: fastcgi_pass unix:/run/php/php7.2-fpm.sock;
, die Nginx anweist, mit dem PHP-Prozess über den Socket namens php7.2-fpm.sock
zu kommunizieren. Bei jeder eingehenden Anfrage schreibt Nginx auch Daten in diese Datei und sendet sie nach Erhalt der Ausgabe zurück an den Browser
Ich muss noch einmal betonen, dass dies nicht das vollständigste oder genaueste Bild der Vorgänge ist, aber für die meisten DevOps-Aufgaben ist es völlig ausreichend
Lassen Sie uns nun rekapitulieren, was wir bisher gelernt haben
- PHP empfängt die von den Browsern gesendeten Anfragen nicht direkt. Webserver wie Nginx fangen diese zunächst ab.
- Der Webserver weiß, wie er sich mit dem PHP-Prozess verbinden kann, und gibt alle Anfragedaten an PHP weiter (er fügt buchstäblich alles ein).
- Wenn PHP mit seiner Arbeit fertig ist, sendet es die Antwort an den Webserver zurück, der sie wiederum an den Client (oder Browser, in den meisten Fällen) weiterleitet.
Oder grafisch dargestellt

Soweit so gut, aber jetzt kommt die Millionen-Dollar-Frage: Was genau ist PHP-FPM?
Das "FPM" in PHP steht für "Fast Process Manager", was nichts anderes bedeutet, als dass das auf einem Server laufende PHP kein einzelner Prozess ist, sondern mehrere PHP-Prozesse, die von diesem FPM-Prozessmanager gestartet, gesteuert und beendet werden. Es ist dieser Prozessmanager, der an den Webserver die Anfragen weiterleitet
Der PHP-FPM ist ein ganzes Kaninchenloch für sich, also erforschen Sie ihn ruhig, wenn Sie möchten, aber für unsere Zwecke reicht diese kurze Erklärung 🙂
Warum PHP-fpm optimieren?
Warum sollten Sie sich auch um all diese Dinge kümmern, wenn alles gut funktioniert? Warum lassen Sie die Dinge nicht einfach so, wie sie sind
Ironischerweise ist das genau der Rat, den ich für die meisten Anwendungsfälle gebe. Wenn Ihr System einwandfrei funktioniert und es keine außergewöhnlichen Anwendungsfälle gibt, sollten Sie die Standardeinstellungen verwenden. Wenn Sie jedoch über einen einzelnen Rechner hinaus skalieren wollen, dann ist es wichtig, das Maximum aus einem Rechner herauszuholen, da dieser die Serverkosten um die Hälfte (oder sogar mehr!) reduzieren kann.
Ein weiterer wichtiger Punkt ist, dass Nginx für die Bewältigung großer Arbeitslasten entwickelt wurde. Es ist in der Lage, Tausende von Verbindungen gleichzeitig zu verarbeiten, aber wenn das nicht auch auf Ihre PHP-Einrichtung zutrifft, verschwenden Sie nur Ressourcen, da Nginx darauf warten muss, dass PHP den aktuellen Prozess beendet und den nächsten annimmt, was die Vorteile, für die Nginx entwickelt wurde, endgültig zunichte macht!
Nachdem das geklärt ist, wollen wir uns nun ansehen, was genau wir ändern, wenn wir es versuchen, php-fpm
zu optimieren
Wie optimiert man PHP-FPM?
Der Speicherort der Konfigurationsdatei für php-fpm
kann je nach Server unterschiedlich sein, so dass Sie etwas suchen müssen. Unter UNIX können Sie den Befehl finden verwenden. Auf meinem Ubuntu lautet der Pfad /etc/php/7.2/fpm/php-fpm.conf
. Die 7.2 ist natürlich die Version von PHP, die ich verwende.
Die ersten Zeilen dieser Datei sehen folgendermaßen aus
;;;;;;;;;;;;;;;;;;;;; FPM-Konfiguration ; ;;;;;;;;;;;;;;;;;;;;; Alle relativen Pfade in dieser Konfigurationsdatei sind relativ zum Präfix der PHP-Installation ; (/usr). Dieser Präfix kann dynamisch mit dem ; '-p' Argument von der Kommandozeile aus geändert werden. ;;;;;;;;;;;;;;;;;; ; Globale Optionen ; ;;;;;;;;;;;;;;;;;; [global] ; Pid-Datei Hinweis: der Standardpräfix ist /var ; Standardwert: keiner pid = /run/php/php7.2-fpm.pid ; Fehlerprotokolldatei Wenn sie auf "syslog" gesetzt ist, wird das Protokoll an syslogd gesendet, anstatt ; in eine lokale Datei geschrieben zu werden.
; Hinweis: Der
Standardpräfix ist /var Standardwert: log/php-fpm.log error_log = /var/log/php7.2-fpm.log
Ein paar Dinge sollten sofort auffallen: Die Zeile pid = /run/php/php7.2-fpm.pid
sagt uns, welche Datei die Prozess-ID des php-fpm-Prozesses
enthält
Wir sehen auch, dass /var/log/php7.2-fpm.log
der Ort ist, an dem php-fpm
seine Protokolle speichern wird
Fügen Sie in dieser Datei drei weitere Variablen wie folgt hinzu
notfall_neustart_schwelle 10 notfall_neustart_intervall 1m prozess_steuerung_zeitüberschreitung 10s
Die ersten beiden Einstellungen dienen der Vorsicht und teilen dem php-fpm
Prozess mit, dass der Hauptprozess von php-fpm
neu gestartet werden soll, wenn zehn Kindprozesse innerhalb einer Minute ausfallen
Das mag sich nicht sehr robust anhören, aber PHP ist ein kurzlebiger Prozess, der viel Speicher verbraucht, so dass ein Neustart des Hauptprozesses bei einer hohen Fehlerquote viele Probleme lösen kann.
Die dritte Option, process_control_timeout
, weist die Kindprozesse an, so lange zu warten, bevor sie das vom Elternprozess empfangene Signal ausführen. Dies ist in Fällen nützlich, in denen die Kindprozesse gerade etwas tun, wenn der Elternprozess z.B. ein KILL-Signal sendet. Wenn sie zehn Sekunden Zeit haben, haben sie eine bessere Chance, ihre Aufgaben zu beenden und sich ordnungsgemäß zu verabschieden.
Überraschenderweise ist dies nicht der Kern der php-fpm
Konfiguration! Das liegt daran, dass php-fpm
für die Bearbeitung von Webanfragen einen neuen Pool von Prozessen erstellt, für den eine eigene Konfiguration gilt. In meinem Fall war der Name des Pools www
und die Datei, die ich bearbeiten wollte, war /etc/php/7.2/fpm/pool.d/www.conf
Lassen Sie uns sehen, wie diese Datei beginnt
; Starten Sie einen neuen Pool mit dem Namen 'www'. Die Variable $pool kann in jeder Direktive verwendet werden und wird durch den ; Poolnamen ('www' hier) ersetzt [www] ; Pro Pool-Präfix Es gilt nur für die folgenden Direktiven: ; - 'access.log' ; - 'slowlog' ; - 'listen' (unixsocket) ; - 'chroot' ; - 'chdir' ; - 'php_values' ; - 'php_admin_values' ; Wenn nicht gesetzt, gilt stattdessen das globale Präfix (oder /usr). Hinweis: Diese Direktive kann auch relativ zum globalen Präfix sein. Standardwert: keiner Präfix = /pfad/zu/pools/$pool ; Unix-Benutzer/Prozessgruppe ; Hinweis: Der Benutzer ist obligatorisch. Wenn die Gruppe nicht angegeben wird, wird die Gruppe des Standardbenutzers ; verwendet. benutzer = www-daten Gruppe = www-data
Ein kurzer Blick auf das Ende des obigen Schnipsels löst das Rätsel, warum der Serverprozess als www-data
läuft. Wenn Sie bei der Einrichtung Ihrer Website Probleme mit den Dateiberechtigungen hatten, haben Sie wahrscheinlich den Eigentümer oder die Gruppe des Verzeichnisses auf www-data
geändert, so dass der PHP-Prozess in der Lage ist, in Protokolldateien zu schreiben und Dokumente hochzuladen, usw.
Schließlich kommen wir zum Kern des Problems, der Einstellung des Prozessmanagers (pm). In dieser Regel sehen die Standardeinstellungen etwa so aus
pm = dynamisch pm.max_children = 5 pm.start_server = 3 pm.min_spare_servers = 2 pm.max_spare_servers = 4 pm.max_requests = 200
Was bedeutet auch"dynamisch" hier? Ich denke, die offiziellen Dokumente erklären dies am besten (ich meine, dies sollte bereits Teil der Datei sein, die Sie bearbeiten, aber ich habe es hier nur für den Fall wiedergegeben, dass dies nicht der Fall ist).
Mögliche Werte: ; statisch - eine feste Anzahl (pm.max_children) von Kindprozessen; ; dynamisch - die Anzahl der Kindprozesse wird dynamisch anhand der ; folgenden Direktiven festgelegt. Bei dieser Prozessverwaltung gibt es ; immer mindestens 1 Kindprozess. pm.max_children - die maximale Anzahl von Kindprozessen, die ; gleichzeitig aktiv sein können. ; gleichzeitig aktiv sein können. pm.start_servers - die Anzahl der Kindprozesse, die beim Start erzeugt werden. pm.min_spare_servers - die minimale Anzahl von Kindprozessen im 'Idle'-Zustand ; (die auf eine Verarbeitung warten). Wenn die Anzahl ; der 'Idle'-Prozesse geringer ist als diese ; Zahl, werden einige Kinder erstellt. pm.max_spare_servers - die maximale Anzahl von Kindern im 'Idle'-Zustand ; (die auf die Verarbeitung warten). Wenn die Anzahl ; der 'Idle'-Prozesse größer als diese ; Zahl ist, werden einige Kinder getötet. ; ondemand - beim Start werden keine Kinder erstellt. Kinder werden geforkt, wenn ; neue Anfragen eingehen. Die folgenden Parameter werden verwendet: ; pm.max_children - die maximale Anzahl der Kinder, die ; gleichzeitig aktiv sein können. pm.process_idle_timeout - Die Anzahl der Sekunden, nach denen ; ein inaktiver Prozess beendet wird. ; Hinweis: Dieser Wert ist obligatorisch
Wir sehen auch, dass es drei mögliche Werte gibt
- Statisch: Eine feste Anzahl von PHP-Prozessen wird beibehalten, egal was passiert.
- Dynamisch: Sie können die minimale und maximale Anzahl von Prozessen angeben, die
php-fpm
zu einem bestimmten Zeitpunkt aufrechterhalten werden soll. - auf Anfrage: Prozesse werden auf Abruf erstellt und zerstört.
Welche Bedeutung haben diese Einstellungen auch?
Einfach ausgedrückt: Wenn Sie eine Website mit geringem Datenverkehr haben, ist die Einstellung "dynamisch" in den meisten Fällen eine Verschwendung von Ressourcen. Angenommen, Sie haben pm.min_spare_servers
auf 3 eingestellt, dann werden drei PHP-Prozesse erstellt und aufrechterhalten, auch wenn auf der Website kein Datenverkehr herrscht. In solchen Fällen ist "ondemand" die bessere Option, die das System entscheiden lässt, wann neue Prozesse gestartet werden sollen
Andererseits werden Websites, die ein hohes Verkehrsaufkommen haben oder schnell reagieren müssen, in dieser Einstellung bestraft. Einen neuen PHP-Prozess zu erstellen, ihn in einen Pool einzubinden und ihn zu überwachen, ist ein zusätzlicher Aufwand, den Sie besser vermeiden sollten
Die Verwendung von pm = static
legt die Anzahl der Kindprozesse fest, so dass die maximalen Systemressourcen für die Bedienung der Anfragen und nicht für die Verwaltung von PHP verwendet werden können. Wenn Sie sich für diesen Weg entscheiden, sollten Sie beachten, dass er seine Richtlinien und Fallstricke hat. Einen recht ausführlichen, aber sehr nützlichen Artikel dazu finden Sie hier
Abschließende Worte
Da Artikel über Web-Performance Kriege entfachen oder zur Verwirrung beitragen können, halte ich ein paar Worte für angebracht, bevor wir diesen Artikel abschließen. Beim Performance-Tuning geht es ebenso sehr um Vermutungen und dunkle Künste wie um Systemwissen
Selbst wenn Sie alle php-fpm-Einstellungen
auswendig kennen, ist der Erfolg nicht garantiert. Wenn Sie keine Ahnung von der Existenz von php-fpm
haben, brauchen Sie keine Zeit damit zu verschwenden, sich darüber Gedanken zu machen. Machen Sie einfach weiter mit dem, was Sie bereits tun, und machen Sie weiter
Vermeiden Sie gleichzeitig, ein Performance-Junkie zu werden. Ja, Sie können eine noch bessere Leistung erzielen, indem Sie PHP von Grund auf neu kompilieren und alle Module entfernen, die Sie nicht verwenden werden, aber dieser Ansatz ist in Produktionsumgebungen nicht vernünftig genug. Die ganze Idee der Optimierung besteht darin, zu prüfen, ob Ihre Bedürfnisse von den Standardeinstellungen abweichen (was selten der Fall ist!), und bei Bedarf kleinere Änderungen vorzunehmen
Wenn Sie nicht bereit sind, Zeit in die Optimierung Ihres PHP-Servers zu investieren, sollten Sie eine zuverlässige Plattform wie Kinsta nutzen, die sich um die Leistungsoptimierung und Sicherheit kümmert.
-
Ich schreibe über, um und für das Ökosystem der Entwickler. Empfehlungen, Anleitungen, technische Diskussionen - was auch immer ich veröffentliche, ich versuche mein Bestes, um Verwirrung und Fluff zu vermeiden und umsetzbare Antworten auf der Grundlage persönlicher Erfahrungen zu geben... mehr lesen