Pour améliorer les performances d’une application sous Symfony 2 et Symfony 3, il y a plusieurs axes. On essaiera d’abord d’améliorer les algorithmes, et d’optimiser les appels à la base de données. Ensuite, pour gagner en réactivité, on pourra se pencher sur le cache.
Il y a un grand nombre de mise en cache possible. Le principe reste cependant le même pour tous :
Le cache, quels usages ?
Dans le diagramme ci-dessus, on souhaite accéder à la « donnée ». Cette donnée peut revêtir plusieurs formes :
- Une image, un document PDF, une feuille de style CSS, un programme javascript
- Une page web statique
- Un morceau de page web
- Un programme ou morceau de programme
- Une donnée issue d’une requête SQL
- Une donnée de l’application, quelle qu’elle soit
Comme on peut le constater, le cache peut se trouver à une multitude de niveaux, et ce sont les développeurs et les administrateurs système qui mettent en place les différents types de cache. En résumé le but du cache est d’accélérer l’accès à la donnée, sans pour autant en perdre la fraîcheur.
Les étapes d’une requête
Le reverse proxy
Lorsqu’on demande une page web, selon l’architecture choisie, la requête va traverser Internet pour aboutir éventuellement sur un serveur reverse proxy. Ce serveur reverse proxy, comme Varnish ou NGINX, va servir les documents statiques pour alléger la charge du serveur d’application.
Le serveur d’application
Le serveur reverse proxy a décidé de demander l’information au serveur d’application. Cela peut être pour un premier accès, ou après que le cache ait été invalidé. Le serveur d’application traite la demande, que ce soit d’une manière simple, en fournissant un fichier statique, ou plus complexe, nécessitant une génération. Il renvoie la donnée au serveur reverse proxy. Dans l’exemple du schéma, le serveur d’application est composé d’Apache Server, et de PHP. Le framework Symfony 2 ou 3 est utilisé pour générer les pages.
- PHP et l’OPCODE
L’OPCODE est le résultat de la compilation d’un script PHP en langage machine. Sans OPCACHE, chaque script PHP est analysé et transformé en langage machine à la volée, puis ce code est exécuté. En mettant l’OPCODE en cache, on évite tout le travail d’analyse du script PHP pour le transformer en langage machine. Depuis PHP 5.6, la mise en cache de l’OPCODE avec OPCACHE est disponible nativement, il suffit de l’activer dans le php.ini. Cette simple activation peut aller jusqu’à gagner en vitesse d’exécution d’un facteur 3 ! Elle est vivement conseillée en production. L’OPCACHE n’a rien à voir avec Symfony, c’est directement au niveau de PHP qu’il agit.
- Symfony 2 ou 3 et les outils de cache
Nous le verrons dans le focus de cet article, Symfony 2 ou 3 est en mesure de s’appuyer sur des outils, intégrés à PHP ou installés à part, pour bénéficier d’un niveau supplémentaire de cache.
C’est ce qu’on appelle le cache utilisateur, ou le cache applicatif.
Retour au navigateur
Lorsque la page web ou les données brutes issues d’un appel AJAX arrivent sur le navigateur, celui-ci peut aussi appliquer une stratégie de cache. L’avantage est que le serveur n’est plus sollicité du tout pour certains appels (au moins temporairement). Grâce au local storage de la norme HTML5, il est possible de stocker des données arbitraires, même après fermeture du navigateur. Si le projet est construit en s’appuyant sur le framework AngularJS, un composant angular-cache permet de stocker des résultats de requêtes de manière transparente pour le développeur, qui n’a alors pas à coder une mise en cache « à la main ».
Le cache dans Symfony 2 ou 3
Un certain nombre d’implémentations de caches applicatifs peuvent être utilisés avec PHP. Lorsqu’on utilise Symfony, des outils existent déjà pour manipuler le cache.
Cache applicatif : deux implémentations
Selon la version de Symfony, la méthode pourra différer, mais globalement, le principe reste le même. Avant Symfony 3.1, la norme PSR-06 n’était pas implémentée par le framework, et on utilisait souvent l’implémentation de Doctrine/cache. A partir de Symfony 3.1, la norme PSR-06 est implémentée, et des outils directement intégrés à Symfony sont disponibles.
Les deux systèmes fonctionnent différemment, les méthodes appelée sont différentes.
Malgré cela, ces deux outils sont agnostiques, c’est-à-dire que la façon de les utiliser ne change pas, alors que le système de cache sous-jacent peut être modifié. Un exemple, lorsque je demande à symfony de stocker la valeur 1000 pour la clé ‘mavaleur’, j’appelle une fonction (save pour les deux). Si je veux récupérer cette valeur, j’appellerai getItem (Symfony cache) ou fetch (Doctrine/cache). Si je décide un jour de passer de APCu à Redis, aucun changement ne sera à opérer dans le code, seule une entrée de configuration suffira.
C’est très pratique, car le développeur n’a pas besoin d’installer de système supplémentaire, la mémoire de l’ordinateur peut suffire par exemple.
Un mot sur l’OPCACHE
Dans le schéma précédent, vous aurez sans doute remarqué deux parties dans le bloc « implémentations ». En effet, deux systèmes XCache et Wincache se démarquent des autres, dans le sens ou ils servent aussi de cache d’OPCODE. En utilisant l’un de ces systèmes, vous ne pourrez pas bénéficier de l’OPCACHE natif de PHP5.6+.
Les autres systèmes ne gère pas le cache d’OPCODE, et se focalisent sur le cache applicatif uniquement. APCu pour sa part, triche un peu, en utilisant l’espace normalement seulement alloué à l’OPCACHE. Les autres systèmes sont complètement indépendants, et peuvent aussi être utilisés par d’autres langages comme Java ou NodeJS.
Mais que fait Symfony ?
Bootstrap.php.cache
Le framework n’est pas en reste. Pour optimiser l’exécution, le framework va s’appuyer sur un fichier « Bootstrap.php.cache » (qui n’a rien a voir avec le Framework homonyme HTML, CCS et JS).
Ce gros fichier contiens toutes les classes PHP du répertoire « vendor », c’est-à-dire toutes les librairie tierces ainsi que le framework lui-même.
Ce fichier va passer à la moulinette de l’interprêteur PHP et être transformé en OPCODE, qui va lui-même être mis dans l’OPCACHE. La boucle est bouclée.
Symfony autoloader
L’autoloader est un traitement qui va faire l’association entre les classes de notre application, et son emplacement sur le disque. Cette association peut être longue et couteuse en entrées-sorties.
N’oublions pas que chaque requête HTTP va lancer ce traitement !
Il est possible de mettre le résultat de ce traitement en cache. Le cache applicatif est ici utilisé. Symfony propose actuellement deux moyen de mettre en cache le résultat du traitement :
- APC (avec APCu) : ApcClassLoader
- XCache : XcacheClassLoader
Notes sur XCache et APCu
XCache est implémenté pour PHP version 5.6 au maximum
Il ne sera manifestement pas reconduit pour PHP7, par manque de temps et de ressource de son développeur. Si vous ne souhaitez pas installer un système de cache comme Redis ou Memcached, il vous reste donc l’option APCu. Et même si vous installez un tel système tiers, je vous conseille d’installer APCu quoi qu’il arrive, afin de bénéficier du cache de l’autoloader Symfony.
Vous l’aurez constaté, la mise en cache passe par différents niveaux. Selon la volumétrie de l’application, et sa fréquentation, il vous sera peut-être nécessaire de cacher certaines données.
Avec de forts trafics, un reverse proxy permet déjà de diminuer la charge pesant sur le serveur d’application, et je ne parle pas ici de load balancing mais de caching uniquement.
Lorsque la requête aboutit sur le serveur d’application, plusieurs caches peuvent entrer en jeu : L’OPCACHE pour les binaires PHP, et un système de cache applicatif.
A noter : il est bien entendu possible de monter tous ces différents systèmes de cache indépendamment les uns des autres. Je peux tout à fait installer Varnish, sans vouloir profiter de l’OPCACHE (dommage) ou d’un cache applicatif. Inversement, je peux activer l’OPCACHE et un cache applicatif sans reverse proxy.
Il faut garder en mémoire que, malgré tous les caches qu’on mettra en place, si le code n’est pas optimisé, des latences se feront sentir. Je pense en particulier aux appels à la base de données avec Doctrine, qui peut être très consommateur, en temps et en ressources machine.
A vos caches, prêts ? Partez !
Cet article a été rédigé par : Arnaud, Lead Dev A5sys