
PHP créé pour mourir. Et tout irait bien, mais il n'a pas eu l'occasion de le faire récemment. Il y a un an, sur le hub, l' annonce de l'outil RoadRunner a eu lieu , forçant le processus PHP à quitter le cercle sans fin de la mort et de la résurrection.
Le principe du travail de RoadRunner est de maintenir le processus en cours et d'y envoyer les requĂȘtes entrantes, ce qui permet, selon les dĂ©veloppeurs, d'augmenter les performances des applications (parfois mĂȘme 40 fois).
Depuis que je travaille avec Magento depuis longtemps, cela semblait ĂȘtre une excellente idĂ©e de tester l'outil non pas sur un cadre mythique, mais sur une application rĂ©elle, pour laquelle Magento Open Source fonctionnait trĂšs bien.
Coût d'initialisation de l'application Magento
Le moyen d'accélérer l'application RoadRunner consiste à réduire le temps de réponse (aprÚs un démarrage de mise en température) en réduisant les frais généraux d'initialisation de l'application.

Capture d'écran de la présentation d'Anton Tsitou "Conception d'applications hybrides Go / PHP à l'aide de RoadRunner"
Dans le cas de Magento, le temps principal consacré au démarrage tombe sur:
- chargement automatique du compositeur
- bootstrapping
Le chargement automatique du compositeur n'attire pas l'attention, car il est standard pour une application PHP.

Profilage des résultats liés à Composer.
L'amorçage de l'application Magento comprend l'initialisation d'un gestionnaire d'erreurs, la vérification de l'état de l'application, etc.
La partie la plus difficile consiste à initialiser le conteneur IoC («ObjectManager» en termes de Magento) et à créer récursivement des instances de dépendance à travers lui pour obtenir l'objet d'application.

Profilage des résultats liés au bootstraping.
Implémentation de RoadRunner
Pour dĂ©marrer RoadRunner, vous devez crĂ©er un travailleur qui contiendra un cycle pour accepter les demandes entrantes et envoyer des rĂ©ponses. De plus, l'outil fonctionne avec les requĂȘtes et rĂ©ponses implĂ©mentant PSR-7. D'aprĂšs la documentation officielle, cela ressemble Ă ceci:
while ($req = $psr7->acceptRequest()) { $resp = new \Zend\Diactoros\Response(); $resp->getBody()->write("hello world"); $psr7->respond($resp); }
Magento et PSR-7
Magento n'a pas encore implĂ©mentĂ© le PSR-7 et prĂȘt Ă l'emploi utilise ses implĂ©mentations de demande et de rĂ©ponse, les approches dans lesquelles elles sont principalement tirĂ©es de la version prĂ©cĂ©dente.
Pour implémenter RoadRunner, vous devez trouver un point d'entrée qui accepterait la demande sous une forme quelconque et renverrait une réponse ( exemple Symfony ).
Il existe un tel point dans Magento , \ Magento \ Framework \ AppInterface , il n'y a qu'un seul problĂšme, cette interface n'est pas conçue pour accepter la demande. Mais attendez, d'oĂč vient-il dans l'application? Il vaut la peine de revenir au dĂ©but et le mantra - PHP est nĂ© pour mourir . En consĂ©quence, la grande majoritĂ© des bibliothĂšques, des packages et des frameworks, lors de la conception et de la division en couches, ne supposent tout simplement pas que la demande se rĂ©vĂšle ĂȘtre plus d'un global.
La couche de transport Magento est construite sur le mĂȘme principe. Bien que la documentation dĂ©crive les diffĂ©rences entre les objets injectables / nouveaux , en fait, nous avons l'utilisation de la requĂȘte comme un service global avec Ă©tat, s'initialisant Ă partir de variables globales ($ _GET, $ _POST). En plus de tout cela, l'injection de ce service est visible Ă tous les niveaux de l'application dans le noyau lui-mĂȘme, sans parler de la qualitĂ© des modules tiers.
Sur la base de ce qui prĂ©cĂšde, l'espoir de mettre en Ćuvre RoadRunner uniquement par la conversion des demandes du style PSR-7 au style Magento a Ă©tĂ© perdu.
Mettre en Ćuvre l'adaptateur PSR-7
Nous formulons le problÚme en tenant compte des informations reçues.
Je voudrais avoir une certaine interface d'application qui accepte une demande PSR-7 et renvoie une réponse PSR-7. Il est également nécessaire de créer une implémentation de l'interface créée qui adapte ce format d'interaction à l'application Magento.

Adaptateur PSR-7
Comme mentionné ci-dessus, l'application magento renvoie déjà une réponse, nous n'avons donc qu'à la convertir au format PSR-7.
Pour la demande, vous avez besoin d'une classe qui procÚde à tous les appels à l'objet de demande actuel, que nous mettons dans un registre spécial (oui, les dieux de l'architecture pardonneront cette perversion). De plus, il a été constaté que la classe de demande n'est pas utilisée une, mais 3, elle nécessite donc leur réinscription via la configuration IoC du conteneur.

L'ensemble de classes utilisĂ© pour travailler avec des requĂȘtes dans Magento
ProblĂšmes d'une application PHP immortelle
Une application lancĂ©e via RoadRunner a les mĂȘmes problĂšmes que tout processus php Ă longue durĂ©e de vie, ils sont dĂ©crits dans la documentation ( https://roadrunner.dev/docs/usage-production )
Les principaux problÚmes du niveau d'application que le développeur doit surveiller sont:
Une attention particuliĂšre dans le contexte de Magento devrait ĂȘtre accordĂ©e Ă la gestion des Ă©tats, car dans le noyau et dans les modules tiers, la mise en cache de l'utilisateur / produit / catĂ©gorie actuel dans le service est une approche trĂšs courante.
protected function getCustomer(): ?CustomerInterface { if (!$this->customer) { if ($this->customerSession->isLoggedIn()) { $this->customer = $this->customerRepository->getById($this->customerSession->getCustomerId()); } else { return null; } } return $this->customer; }
Un exemple d'une méthode du noyau utilisant l'état d'un objet.
Exécution de Magento Rest API Server via RoadRunner
Compte tenu des problÚmes potentiels avec l'état global, sur la base de l'expérience de développement de la partie front-end de Magento, la partie WebApi la plus appropriée et indolore a été choisie pour le lancement.
La premiÚre chose à faire est de créer notre travailleur, qui passera par RoadRunner et vivra sans fin (presque). Pour ce faire, prenez un morceau de code des guides RoadRunner et ajoutez-y notre application, enveloppée dans un adaptateur PSR-7.
$relay = new StreamRelay(STDIN, STDOUT); $psr7 = new PSR7Client(new Worker($relay)); $bootstrap = \Magento\Framework\App\Bootstrap::create(BP, []); $app = $bootstrap->createApplication(\Magento\Framework\App\Http::class); $psr7Application = $bootstrap->getObjectManager()->create( \Isxam\M2RoadRunner\Application\MagentoAppWrapper::class, [ 'magentoApp' => $app ] ); while ($request = $psr7->acceptRequest()) { try { $response = $psr7Application->handle($request); $psr7->respond($response); } catch (\Throwable $e) { $psr7->getWorker()->error((string)$e); } }
Le code avant la boucle while sera exécuté au début du travailleur, tout ce qui se trouve à l'intérieur de la boucle - à chaque nouvelle demande.
Lorsque notre collaborateur est prĂȘt, nous procĂ©dons Ă la configuration du serveur RoadRunner Ă©crit en Go. Tout est agrĂ©able et rapide ici, seul le package du compositeur qui tĂ©lĂ©charge le fichier exĂ©cutable n'a pas besoin d'ĂȘtre installĂ©, ce qui ne peut pas ĂȘtre agrĂ©able. Nous crĂ©ons la configuration de notre serveur - le plus simple ressemble Ă ceci.
http: address: 0.0.0.0:8086 workers: command: "php worker.php" pool: numWorkers: 1
Configuration de RoadRunner.
La documentation contient une abondance de paramÚtres qui vous permettent de configurer le serveur de maniÚre flexible afin que le désir de compiler votre binaire ne se réalise pas exactement.
./rr serve -v -d
Démarrage du serveur
Test de solution
Les outils
Pour des tests pratiques, nous prenons quelque chose de simple, par exemple artillery.io.
Nous testerons les performances Ă l'aide d'un utilisateur exĂ©cutant des requĂȘtes sĂ©quentiellement (RoadRunner prend Ă©galement en charge l'exĂ©cution de requĂȘtes dans plusieurs threads, nous laisserons cette question Ă d'autres chercheurs)
En entrĂ©e, nous avons le fichier de configuration d'artillerie avec deux environnements - Apache et RoadRunner. Ils fonctionnent tous les deux avec la mĂȘme instance Magento, donc ici, ils sont sur un pied d'Ă©galitĂ©.
Scénarios de test
Les scénarios suivants ont été utilisés pour mesurer les performances des deux solutions.
Scénario 1. Création d'une catégorie - name: "S1. Create category" flow: - loop: - post: url: "/rest/V1/categories" json: category: name: "name-{{prefix}}-{{ $loopCount }}" parent_id: 2 is_active: true count: 100
Scénario 2. Obtenir une liste de pays - name: "S2. Countries list" flow: - loop: - get: url: "/rest/V1/directory/countries" count: 100
Scénario 3: répertorier les types de produits - name: "S3. Product types list" flow: - loop: - get: url: "/rest/V1/products/types" count: 100
Scénario 4. Obtention d'une liste d'ensembles d'attributs - name: "S4. Product attribute sets list" flow: - loop: - get: url: "/rest/V1/products/attribute-sets/sets/list?searchCriteria" count: 100
Scénario 5. Obtenir une catégorie - name: "S5. Category get" flow: - loop: - get: url: "/rest/V1/categories/2" count: 100
Scénario 6. Création de produit - name: "S6. Create product" flow: - loop: - post: url: '/rest/V1/products' json: product: sku: "sku-{{prefix}}-{{ $loopCount }}" name: "name-{{prefix}}-{{ $loopCount }}" attribute_set_id: 4 price: 100 type_id: "simple" count: 100
Scénario 7. Récupération d'une liste de produits - name: "S7. Get product list" flow: - loop: - get: url: "/rest/V1/products?searchCriteria[pageSize]=20" count: 100
Résultat
AprĂšs avoir exĂ©cutĂ© tous les scripts alternativement via RoadRunner et Apache, les mĂ©dianes de la durĂ©e de la requĂȘte ont Ă©tĂ© obtenues. Selon les mĂ©dianes, la vitesse de tous les scĂ©narios diffĂšre d'environ la mĂȘme valeur de ~ 50 ms.

Résultat du test de performance.
Résumé
Une expérience pratique a confirmé les hypothÚses sur la constance du gain de performances RoadRunner sur une application spécifique. L'utilisation de cet outil vous permet d'accélérer le traitement des demandes d'application pour une constante égale au temps d'initialisation de l'environnement et des dépendances.
Sur les manipulateurs légers, cela vous permet d'accélérer parfois l'application, sur les lourds, cela ne donne presque pas d'effet tangible. Si votre code est lent, alors le plantain ne l'aidera probablement pas.
Si votre application est bien Ă©crite, il n'y aura probablement aucun problĂšme avec son fonctionnement via RoadRunner, mais si l'application nĂ©cessite une adaptation pour ĂȘtre utilisĂ©e avec RoadRunner, alors il aurait probablement fallu la mĂȘme chose sans RoadRunner pour observer plus clairement les couches de l'architecture. et suivre les normes de dĂ©veloppement dans le domaine.
Magento Open Source dans son ensemble est adaptĂ© au lancement dans l'environnement donnĂ©, cependant, il nĂ©cessite des amĂ©liorations de la couche de transport et la correction de la logique mĂ©tier pour Ă©viter un comportement incorrect lors de demandes rĂ©pĂ©tĂ©es au sein du mĂȘme processus. En outre, l'utilisation de RoadRunner impose certaines restrictions aux approches de dĂ©veloppement, cependant, elles ne contredisent pas les pratiques bien Ă©tablies.
Enfin une belle capture d'écran. Quand verrez-vous toujours les demandes Magento avec ce temps de réponse? Les références
- Exemple de l'article
- Site officiel de RoadRunner