Preload in PHP 7.4: Composer und Auswahl von Dateien für das Preload

Wir bei Badoo stellen aktiv auf PHP 7.4 um und sind sehr begeistert von der Möglichkeit, die neue Preload-Funktion zu nutzen. Vor nicht allzu langer Zeit haben wir über unsere Experimente mit ihr gesprochen.

Anscheinend ist die Community genauso aufgeregt wie wir. Framework-Entwickler diskutieren aktiv die Möglichkeit der Einführung eines Preloads (und einige haben dies bereits unterstützt ). Jetzt ist der Abhängigkeitsmanager von Composer an der Reihe.



Italo Baeza schrieb einen Artikel, in dem er seine Meinung darüber äußerte, wie Composer mit der Vorspannung arbeiten sollte. Ich beschloss, eine Übersetzung dieses Textes und gleichzeitig eine Übersetzung seines anderen Artikels darüber zu teilen, wie die Composer-Entwickler selbst auf den Vorschlag geantwortet haben, sowie über ein neues Tool, das die Arbeit mit dem Preload erleichtert.

Wie sollte Composer in PHP 7.4 vorinstalliert werden?


Preload ist eine der wichtigen Funktionen, die PHP 7.4 Entwicklern bietet, die eine bessere Leistung benötigen. Diese Funktion kann als "Aufwärmen" bezeichnet werden, bevor die JIT-Engine implementiert wird, die in PHP 8 angezeigt wird (oder angezeigt werden sollte). Vorher reicht das Vorabladen aus, und wer weiß, vielleicht können sie zusammenarbeiten.

Was die Vorladefunktion ist, wird in diesem Artikel erklärt . Das Fazit ist sehr einfach: php.ini gibt ein PHP-Skript an, für das beim Start des Prozesses Dateien in den Speicher geladen werden (Preload). In Kombination mit OPCache und der Autoloader-Funktion können Composer-Dateien auch einmal kompiliert und verknüpft werden. Danach stehen sie für alle nachfolgenden Anforderungen zur Verfügung. Dadurch muss PHP nicht bei jeder Anforderung Dateien herunterladen und kompilieren.

Die Composer-Entwickler haben sich jedoch nicht darauf geeinigt, wie das Vorladen zusätzlich zu den Startfunktionen unterstützt werden soll. Die Fakten sind wie folgt:

  • Preloading wurde erstmals in PHP 7.4 angekündigt;
  • Es gibt keine Composer-Direktive, mit deren Hilfe Dateien vorab geladen werden können.
  • Zum Vorabladen benötigen Sie Zugriff auf die php.ini, dh auf den Prozess selbst.
  • Durch das Vorladen aller Dateien wird die Leistung nicht unbedingt verbessert, verglichen mit dem Vorladen nur der am häufigsten angeforderten Dateien.

Mit anderen Worten, nur diejenigen, die vollen Zugriff auf die Server haben, können das Preloading verwenden. Dies schließt gemeinsam genutzte Server und einige PaaS-Lösungen aus, bei denen nicht mit php.ini gearbeitet wird.

Wie kann Composer beim Vorladen helfen, da dies eine Innovation ist? Hier ist meine Meinung.

Wie sollte die Vorspannung funktionieren?


Der Mechanismus zum Vorabladen sollte auf einer Liste von Dateien basieren, die beim Start geladen und im Speicher abgelegt werden. Und da dies eine Liste ist, müssen wir mit einem Array von Dateien arbeiten und Composer die ganze Arbeit überlassen, anstatt jede Datei manuell zu laden .

Composer sollte die Liste der von der Anwendung angegebenen Dateien (das Stammprojekt) nehmen und alles in Dateien kompilieren, die PHP problemlos verwenden kann.
Gleichzeitig müssen wir in der Lage sein, Pakete zum Preload-Mechanismus hinzuzufügen und daraus zu entfernen.
Das Vorladen sollte niemals auf Paketebene funktionieren, da es in der Verantwortung des Entwicklers liegt, das Vorladen jedes Pakets zu aktivieren oder zu deaktivieren.

Das Vorladen in Composer sollte optional sein. Der Entwickler muss in der Lage sein, es zu deaktivieren, damit PHP seinen eigenen Preloader verwendet, der auf der Grundlage der OPCache-Analyse arbeiten kann. Er hängt von der Anwendungslast ab und arbeitet wesentlich effizienter als das einfache Vorladen aller Dateien.

Alles beginnt bei preload.json


Um das System nicht zu komplizieren, legen Sie die Datei preload.json im Stammverzeichnis des Projekts ab. Daraufhin werden die Vorladedateien aufgelistet, die Composer auswählen kann. Da es sich um eine JSON-Datei handelt, kann der Entwickler sie mithilfe eines speziellen Befehls generieren. Ich denke, es wäre großartig, wenn Composer ein Hilfsprogramm zum Erstellen einer solchen skriptbasierten JSON-Datei hätte.

{ "pre-compile": [ "my-script.php", "my-other-script.php" ], "extensions": [ "php" ], "files": [ "app/*", "config/", "helpers.php", "app/Models/*", "app/Controllers/*/Http/*", "app/Views/Compiled*.php" ], "namespace": [ "App\\Models", "App\\Controllers\\", "App\\Views\\MainView", "Vendor\\Package\\*", ], "packages": { "symfony/http-client": true, "robert/*-client": true, "vendor/package": { "files": true, "namespace": true }, "foo/bar": { "files": [ "helpers.php", "loaders/*" ], "namespace": [ "Foo\\Bar\\DynamicLoaders\\*", "Foo\\Bar\\Clients" ] } }, "output": "preload-compiled.php" } 

Mit preload.json können Sie schnell überprüfen, ob das Vorladen im Projekt enthalten ist: Wenn die Datei fehlt, wird das Vorladen nicht unterstützt oder ist unerwünscht.

Mal sehen, was die Tasten tun.

vorkompilieren

Diese Dateien werden von Composer ausgeführt. Jedes Skript muss ein Array absoluter Dateipfade zurückgeben, um sie der Preload-Liste hinzuzufügen, die die Rolle der Hauptliste übernimmt.

 "pre-compile": [ "my-script.php", "my-other-script.php" ] 

Diese Dateien werden in der angegebenen Reihenfolge ausgeführt.

Ziel ist es, dass der Entwickler eine Liste von Dateien erstellt, die er für richtig hält, anstatt sich auf eine einzelne JSON-Datei zu verlassen. Diese Dateien werden zuerst ausgeführt. Und ja, Sie können preload.json nur mit diesem Schlüssel implementieren. Da es sich um PHP-Dateien handelt, können Sie beim Kompilieren eines Arrays sogar andere Dateien hinzufügen.

Erweiterungen

Dies ist eine Liste der Dateierweiterungen, die vorab geladen werden müssen. Standardmäßig werden nur Dateien mit der PHP-Erweiterung verwendet.

 "extensions": ["php", "php5", "php7"] 

Sie können beispielsweise ein Verzeichnis hinzufügen, das mit * .phtml-Dateien gefüllt ist, einschließlich einiger nützlicher PHP-Dateien, und Composer wählt nur diese und nicht den gesamten Inhalt des Verzeichnisses aus.
Wie Sie verstehen, kann dieser Vorgang durch manuelles Hinzufügen von Dateien ersetzt werden.

Dateien

Dieser Schlüssel weist Composer an, alle Dateien aus der Liste herunterzuladen, deren Pfade sich auf den Speicherort von composer.json beziehen.

 "files": [ "helpers.php", "app/Models/*", "app/Controllers/*/Http/*", "app/Views/Compiled*.php", ] 

Die Liste herauszufinden ist einfach:

  • Verwenden Sie relative Pfade, um Dateien und Verzeichnisse hinzuzufügen.
  • Aus Verzeichnissen werden nur untergeordnete Dateien hinzugefügt (nicht rekursiv).
  • rekursive Pfade werden durch ein Sternchen (*) gekennzeichnet;
  • Mit diesem Symbol können Sie beispielsweise auch bestimmte Dateien und Verzeichnisse hinzufügen: src/Clients/*/Stores oder src/Model*.php .

Das Hinzufügen von Dateien per Maske ohne manuelle Auswahl oder Erstellung von anwendungsspezifischen Skripten ist besonders nützlich, wenn Sie große Anwendungen entwickeln.

Wenn Sie nur alle Dateien mit dem Autoload-Schlüssel in der Composer-JSON-Datei vorab laden müssen, setzen Sie ihn auf true .

Namespace

Dieser Schlüssel weist Composer an, Dateien mit einem bestimmten Namespace oder Klassennamen wie file oder directory zu laden. Mit demselben Mechanismus können Sie Space-Namen aus anderen installierten Paketen dynamisch aufrufen.

 "namespaces": [   "App\\Models",   "App\\Controllers\\",   "App\\Views\\MainView",   "Vendor\\Package\\*", ] 

Dies ist auch praktisch, wenn Sie mit großen Anwendungen arbeiten, die stärker von Namespaces abhängig sind, als von Dateien, die sich jederzeit ändern können. Composer extrahiert Dateien automatisch gemäß dem Namespace und fügt sie in eine Liste ein.

Pakete

Mit diesem Schlüssel können Sie andere Dateien laden, die aus externen Paketen registriert wurden, z. B. Hilfsdateien oder Klassen, die einem Namespace zugeordnet sind.

 "packages": {   "symfony/http-client": true,   "robert/*-client": true,   "vendor/package": {       "files": true,       "namespace": true   },   "foo/bar": {       "files": {           "helpers.php",           "loaders/*"       },       "namespace": [           "Foo\\Bar\\DynamicLoaders\\*",           "Foo\\Bar\\Clients"       ]   } } 

Hier ist alles ganz einfach: Wenn der Wert true ist, wird der gesamte Inhalt des Autoload-Schlüssels in der Datei composer.json dieses Pakets geladen. Andernfalls können Sie die Vorspannungsaddition genauer steuern.

Wenn der Schlüsselwert true ist, werden alle beim autoload Laden registrierten Dateien heruntergeladen. Der Standardwert ist false . Dies gilt auch für den namespace Schlüssel.

Sie können mit dieser Regel auch einzelne Dateien oder Namespaces auswählen. In diesem Fall wird der autoload Schlüssel jedoch nicht verwendet.

Ausgabe

Dies ist einfach der Name der kompilierten Preload-Listendatei.

 "output": "preload-compiled.php" 

Einfache Montage


Unsere Preload-Liste ist fertig und wir können Composer aufrufen, um das Haupt-Preload-Skript zu kompilieren.

 composer preload 

Infolgedessen wird preload-compiled.php mit allen Dateien erstellt, die PHP vorladen muss. Natürlich können Sie den Dateinamen beliebig ändern.

Sie müssen auch die preload Schlüssel preload Parametern überschreiben.

 composer preload \   --input=my-custom-preload-list.json \   --output=my-preload.php 

Standardmäßig deaktiviert


Projekte ohne preload.json geben einen Fehler zurück, wenn Sie versuchen, eine Datei zum Vorladen zu kompilieren. Der Grund dafür ist, dass Composer nicht erraten wird (und sollte), was vorinstalliert werden soll.

Ich möchte Sie daran erinnern, dass das Vorladen die normale Funktionalität von Composer nicht beeinträchtigt. Da dies ein Konsolenbefehl ist, können Sie bei lokaler Entwicklung das Vorladen vollständig abbrechen. Das einzige, was der Composer-Vorlademechanismus benötigt, ist eine Autoload-Datei, die generiert werden sollte, wenn sie fehlt. Immerhin ist fast das 2020. Jahr in der Werft, PSR-4 wird überall eingesetzt, oder?

Ergebnis


Sie sollten eine PHP-Datei mit so etwas bekommen:

 <?php /** * Preloading @generated by Composer */ // Autoload the classes so those can be preloaded using `require_once`. require_once __DIR__.'/../autoload.php'; // File list $files = [ '/var/www/app/Foo.php', '/var/www/app/Bar.php', '/var/www/helpers/basic.php', '/var/www/helpers/advanced.php', '/var/www/vendor/Foo/Bar/src/Class.php', '/var/www/vendor/Foo/Bar/helpers/helpers.php', '/var/www/vendor/Foo/Bar/config.php', // ... ]; // Preload all root project files foreach ($files as $file) { require_once $file; } 

Tatsächlich ist dies nur eine Liste von Dateien, die mithilfe der Autoloader-Funktion in Composer vorgeladen werden. PHP wird diese Datei einmal ausführen und es wird Geschichte.


Ich hoffe aufrichtig, dass Composer die Möglichkeit hat, Dateien vorab zu laden, ohne einen Hack schreiben zu müssen.

Da die oben beschriebene Methode nicht Teil des Composer-Kernels ist, können Sie dennoch die wichtigsten Dateien für das Vorladen auf der Grundlage der OPCache-Analyse auswählen, ohne die weniger benötigten zu berühren. Anstatt 1.500 Dateien mit einer Kapazität von 100 MB vorab zu laden, können Sie nur 150 Dateien mit einer Kapazität von 10 MB herunterladen und dabei 99% der ursprünglichen Leistung beibehalten.

Wir laden das PHP 7.4-Projekt in einer Zeile vor


Kurz nachdem ich einen Artikel darüber geschrieben hatte, wie Composer Sie beim Vorabladen eines Projekts unterstützen kann, hat Seldaek (ein Mitglied des Composer-Entwicklungsteams) die Hoffnung zunichte gemacht , dass Composer eine einfache Option zum Vorabladen des Projekts in den PHP-Prozess vom Paket-Manager aus haben würde.

(...) Ich werde erklären: Ich bin mir sicher, dass wir in naher Zukunft nichts hinzufügen werden, was mit dem Vorladen von Composer zu tun hat.

Warum? Das Vorabladen in PHP ist eher ein Entwicklungsproblem (als ein Abhängigkeitsproblem) und wird durch manuelles Bearbeiten der Datei php.ini gelöst. Dies können nur Entwickler, die PHP selbst verwalten.

Dies hindert mich jedoch nicht daran, ein eigenes Paket zum Vorabladen des Projekts zu erstellen. Und du auch.

Vorspannung und Metriken


Das Vorladen kann ein gutes Werkzeug für eine einfache und kostengünstige Steigerung der Produktivität ohne ernsthafte Verarbeitung sein.

Aber das Problem ist nicht, wie man vorlädt, sondern was . Durch das Vorladen ganzer Frameworks und Tausender von Dateien wird der Arbeitsspeicher schnell erschöpft, sodass es zumindest in großen Projekten nicht möglich ist, dies blind zu tun. Es wird empfohlen, nur die am häufigsten angeforderten Dateien herunterzuladen. Aber wie definiert man sie?

Glücklicherweise ermöglicht OPCache opcache_get_status () , Daten darüber zu sammeln, auf welche Dateien am häufigsten zugegriffen wird. Sie können nicht nur herausfinden, welche Dateien am meisten nachgefragt werden, sondern auch, wie viel Speicher sie einige Zeit nach dem Start der Anwendung verbrauchen.

Es wird empfohlen, entweder eine Woche oder bis zu dem Zeitpunkt zu warten, an dem OPCache eine bestimmte Anzahl von Treffern registriert. Es hängt alles von der Anwendung ab, aber Sie bekommen den Punkt.
Erstellen wir also eine Preload-Liste basierend auf den Statistiken der beliebtesten Dateien. Ich habe ein Paket dafür gemacht.

Stellen Sie sich vor ... Preloader!


Dieses Paket erstellt automatisch eine Vorladeliste für Ihre Anwendung. Es werden OPCache-Nutzungsstatistiken erfasst, die Dateien nach der Anzahl der Treffer sortiert und eine Liste erstellt, sodass die Gesamtdateigröße den angegebenen Schwellenwert nicht überschreitet.



Ich habe lange nach der besten Strategie für die Erstellung einer Liste gesucht. Und ich bin zu dem Schluss gekommen, dass es am besten ist, alle Dateien hinzuzufügen, bis das Speicherlimit erreicht ist, das für Pakete standardmäßig 32 MB beträgt. Dateien werden nach der Anzahl der Treffer sortiert und das Paket wird automatisch ausgeschlossen.

Mit anderen Worten, PHP verbessert die Anwendungsleistung bei der Verarbeitung der meisten Anfragen.

Und wie benutzt man es? Teilen Sie Composer Autoloader mit, wo das Preloader-Skript geschrieben werden soll, und fertig.

 use DarkGhostHunter\Preloader\Preloader; Preloader::make() ->autoload('vendor/autoload.php') ->output('preload.php') ->generate(); 

Natürlich müssen Sie auswählen, wann generiert werden soll, aber das ist der springende Punkt. Sie können dies sogar nach dem Zufallsprinzip tun und die Liste beispielsweise für jede 100. Anforderung neu schreiben.

 use DarkGhostHunter\Preloader\Preloader; Preloader::make() ->whenOneIn(100) ->autoload('vendor/autoload.php') ->output('preload.php') ->overwrite() ->generate(); 

Sie erhalten ein fertiges Preload-Skript, das Sie in die php.ini einfügen können.

 <?php /** * This file is generated automatically by Preloader. * * This script uses Composer Autoload file and `require_once` to preload the files in this * list. Add this file to your `php.ini` in `opcache.preload` to preload this list into * PHP at startup. Additionally, this file also includes information about Opcache. * * * Add (or update) this line in `php.ini`: * * opcache.preload=/www/app/vendor/preload.php * * --- Config --- * Generated at: 2019-11-20 15:20:49 UTC * Opcache * - Used Memory: 130585 B * - Free Memory: 294896 B * - Wasted Memory: 347764 B * - Cached files: 2675 * - Hit rate: 94% * - Misses: 542 * Preloader config * - Memory limit: 32 MB * - Overwrite: false * - Files excluded: 0 * - Files appended: 0 */ require_once '/www/app/vendor/autoload.php'; $files = [ '/www/app/ClassFoo.php', '/www/app/ClassBar.php', '/www/app/ClassBaz.php', '/www/app/ClassQuz.php', '/www/app/ClassQux.php', '/www/app/vendor/author/package/src/Example.php', // ... ]; foreach ($files as $file) { require_once $file; } 

Und alle. Probieren Sie es aus: darkghosthunter / preloader - Packagist .

Source: https://habr.com/ru/post/de480746/


All Articles