Et à mon grand désarrois, après rechargement de la page, j'ai bien été obligé de constater que cela n'avait strictement rien changé, bien au contraire, puisque les performances s'étaient encore plus dégradées.
Après avoir lancé un Putain merde fait chier !
bien senti dans le bureau afin d'évacuer un peu ma colère, j'ai donc cherché à comprendre ce qui pouvait bien se passer pour que ma solution ne donne pas le résultat escompté.
Mon premier réflexe a été de faire un appel à la commande top
afin de définir le processus qui s'arrogeait l'ensemble des ressources du serveur et qui provoquait donc le ralentissement constaté.
Et si le changement d'architecture n'a pas augmenté les performances, il m'a cependant permis de constater que le coupable n'était pas Apache 2 mais bien PHP !
En effet, php-fpm permet de découpler totalement les processus PHP des processus de Apache 2, et cette granularité supplémentaire m'a permis de constater qu'un seul et unique processus PHP monopolisait totalement le serveur alors que les autres attendaient.
De plus, comme nous avions alloué un groupe de processus au site de e-commerce et un autre groupe distinct au site d'administration, nous avons pu conclure que le code responsable du ralentissement de l'ensemble de la solution provenait du site d'administration.
La mise en place de php-fpm n'avait donc pas résolu le problème de verrouillage de ressource introduisant une situation de compétition que j'avais diagnostiqué auparavant, mais elle m'avait cependant permis de confirmer mon diagnostique et de déterminer plus précisément le coupable.
Dans ce genre de situation, il faut mieux voir les aspects positifs, histoire de ne pas perdre le moral en se disant que nous venions de passer une journée de travail par les pertes et profits…
Cerise sur le gâteau, j'avais maintenant un numéro de processus à donner à manger à la commande strace
qui ne regroupait pas à la fois Apache et PHP, et en conséquence, je pouvais avoir une visibilité plus nette et précise sur ce que pouvait bien être en train de faire ce processus trop gourmand.
Encore une fois, dans ce genre de situation, il faut vraiment voir le bon côté des choses…
La commande strace
m'a donc permis d'obtenir la liste des appels systèmes effectué par le processus PHP en question, et j'ai alors été proprement estomaqué par le nombre d'appels au système de fichiers effectué par le processus.
En effet, ils représentaient à eux seuls la grande majorité du dialogue entre le processus et le système d'exploitation.
En résumé, le processus semblait passer son temps à demander au système de fichier si tel ou tel fichier existait bien, et dans l'affirmative, de lui en renvoyer le contenu, ce qui correspond au comportement typique de PHP lorsqu'on lui demande d'inclure un fichier notamment à l'aide des instructions include_once
et require_once
.
Une analyse un peu plus attentive nous a permis de constater qu'une partie d'entres eux était induite par une mauvaise configuration de PHP.
En effet, pour chaque fichier demandé par le mécanisme d'auto-chargement de classe ou via include
et ses dérivés, PHP regardait tout d'abord dans le répertoire correspondant à pear et dans un autre répertoire sans aucun rapport avec notre code.
Et pour cause, la directive include_path
défini par le fichier php.ini
était initialisée avec les valeurs par défaut telles que définies lors de l'installation de PHP.
Nous avons donc corrigé la valeur de cette directive, et nous avons alors constaté une baisse significative du nombre d'appel au système de fichiers, sans pour autant que le volume de ces appels devienne raisonnable.
Nous avons alors commencé à nous intéresser au mécanisme d'auto-chargement de classe du site d'administration, et nous avons découvert que pour des raisons historiques, il était conçu de façon à pouvoir faire cohabiter plusieurs frameworks et qu'en conséquence, il lui était nécessaire d'effectuer un grand nombre d'appels au système de fichier pour localiser le fichier correspondant à la classe qui lui était demandé.
J'ai alors commencé à faire un peu de ménage, car certains mécanismes mis en place étaient redondants, et j'ai remplacé les appels à require_once
et include_once
respectivement par require
et include
lorsque cela était pertinent.
Mais malheureusement, cela n'a pas été suffisant puisque je ne suis pas parvenu à obtenir une baisse suffisante du nombre d'appels au système de fichiers.
Bien que je ne sois pas fan de ce genre de solution, je me suis donc résolu à développer le code nécessaire pour mettre en cache les chemins permettant d'accéder à chaque classe du site d'administration, afin que PHP n'ai plus besoin de multiplier les appels au système de fichiers pour localiser la classe demandée.
Une fois cela effectué, nous avons alors constaté une baisse très importante du nombre d'appel au système de fichier et une légère amélioration des performances.
Mais surtout, la suppression de ces appels nous a permis d'avoir à l'aide de strace
une vision plus précise de ce que faisait notre fameux processus PHP.
Et là, d'un coup, j'ai eu l'illumination !
11 réactions
1 De raphaelle - 07/11/2012, 14:06
Merci pour ton article, vivant et intéressant. J'aimerais réussir à trouver la fin avant, comme dans les films, mais là je sèche...on a droit à un indice supplémentaire?
2 De Devart - 07/11/2012, 14:11
Rofl,
première fois que je lis un article aussi prenant, vite vite la suite
Pour le soucis => ext4 barrier ?
Pas d' APC sur la machine ?
3 De Rémi JANOT - 07/11/2012, 14:57
Bon, je suis déçu car j'ai pas encore de réponse à ce questionnement, cependant, j'aime beaucoup ce billet car il décrit une façon et une logique de débuggage que j'aime beaucoup.
Merci !
4 De Laurentj - 07/11/2012, 15:17
Tiens, cette histoire de problème de perf à cause d'autoloader à gogo me rappelle quelque chose... http://ljouanneau.com/blog/post/201...
5 De Eric D - 07/11/2012, 16:23
Raaaah c'est pas possible un suspense pareil ! Surtout avec autant de temps entre les articles. Allez, quoi, on se demande tous d'où venait finalement cet étranglement.
6 De Cyrano - 07/11/2012, 21:34
Intéressant, et ça m'inspire même un truc à propos de l'application que je construis depuis pas mal de temps. Je crois bien que je sais où je vais regarder dans très peu de temps.
Mais j'ai quand même également très hâte d'avoir la suite
À quand la version cinématographique ? :P
7 De bricet - 12/11/2012, 10:35
Cette bataille à coup de strace me rappelle une histoire qui nous a permis d'apporter une modification à Jelix (nous avons identifié le problème de notre côté et notre bien aimé Laurent avait patché avec talent ).
C'est donc http://developer.jelix.org/ticket/1...
Grosso modo, PHP 5.3.0 introduit des arguments supplémentaires possibles à clearstatcache(), qui permettent de vider le cache interne à PHP, mais en sélectionnant éventuellement un fichier unique (et donc supprimer le cache qui lui est associé, mais pas le cache associé aux autres fichiers). Les gains en performance peuvent être appréciables
8 De MathRobin - 08/12/2012, 10:51
Hello ! Je viens de finir de lire cette chronique (oui je suis un peu en retard sur mes lectures). J'ai envie de dire, et la suite ? Où qu'elle est la suite ?
Comprends pas, c'était quoi l'illumination ? Je suis curieux :D
Comme toujours, style intéressant, j'ai l'impression de lire un roman policier. Marde, c'est quoi le mobile du colonnel Process Moutarde ?
Tcho !
9 De mageekguy - 08/12/2012, 14:56
@MathRobin : Il y a encore deux épisodes de prévus, mais je manque de temps en ce moment.
10 De MathRobin - 08/12/2012, 15:04
@mageekguy : Tant que tu nous fais un petit passage à la Inspecteur Harry, ça me dérange pas d'attendre encore un peu
11 De ovh - 19/12/2012, 13:17
Perso, j'aurais attendu d'avoir terminé toute la rédaction avant de publier les épisodes, parce que là, c'est quand même vachement frustrant...
Je comprends tout à fait que tu n'aies pas le temps et je ne t'en fais aucun reproche, mais dans ce cas je préfère ne rien voir publié plutôt que d'avoir un épisode tous les 6 mois
Mais bon, ton blog étant intéressant par ailleurs, je continue de le rafraîchir tous les jours à la pause midi :D