On sait depuis longtemps que la gestion du cache est l’arme fatale pour construire un site performant. Mais la mise en œuvre d'un cache performant dans le contexte de sites complexes ou de portails peut nécessiter des techniques et des outils spécifiques.
En grande majorité, les sites servent les mêmes pages à tout le monde, et en grande majorité, ces pages sont construites par un CMS ou une application dédiée, au moyen d’un gabarit, dans lequel sont insérés les contenus extraits d’une base de données. L’élaboration des pages est un travail important, qui consomme beaucoup de ressources, et peut prendre jusqu’à 1/10ème de seconde. Mais puisque la page a déjà été construite quelques secondes plus tôt pour un autre internaute, inutile de tout recommencer, il suffit de l’avoir conservée en mémoire, prête à l’emploi.
C’est donc le principe du cache, qui peut multiplier par 100, voire 1000, la capacité d’accueil d’une plateforme Internet, et il existe depuis longtemps d’excellents outils open source pour mettre en place un cache, ceci quel que soit le CMS, ou bien l’application en général, qui construit les pages. Le gestionnaire de cache est placé devant le serveur http, et reçoit les requêtes des internautes ; si la page demandée est dans son cache, il la sert directement, sinon, il la demande au serveur, puis la range dans son cache, pour un éventuel usage futur. Dans ce dispositif, chaque page peut comporter sa propre indication de durée de vie, durée pendant laquelle elle peut être servie à partir du cache, avant d’être rafraîchie.
Les outils de cache standards de l’open source
L’outil le plus célèbre est Squid, un serveur proxy utilisé le plus souvent en mode reverse proxy c’est à dire en tant que serveur de cache. Squid remonte aux débuts du web, avec une version 1 qui date de 1996. C’est un projet qui a peu évolué, pendant pas mal d’années, mais qui s’est semble-t-il réveillé depuis 6 mois. Varnish est un outil plus récent, également open source, qui cible directement la fonction de cache, et annonce des performances sensiblement supérieures.
La mémoire ne coûte pas cher
Un dispositif de cache fonctionne le plus souvent selon un principe de MRU, « Most Recently Used », c’est à dire que les contenus les plus récemment utilisés sont conservés en mémoire. Si la mémoire allouée ne suffit plus, alors on laisse tomber les contenus qui n’ont pas été utilisés depuis longtemps, les « Least Recently Used ». Le principe est bien sûr que les contenus les plus utilisés seront toujours statistiquement dans les plus récemment utilisés, et resteront donc toujours en cache. Cette gestion MRU permet d’allouer une certaine quantité de mémoire à la gestion du cache, et d’obtenir le meilleur taux de succès, c’est à dire le pourcentage des pages qui peuvent être servies depuis le cache, que l’on appelle hit ratio. Bien entendu, plus la mémoire allouée augmente, plus le hit ratio augmente, ceci jusqu’à ce que la mémoire allouée au cache atteigne la somme totale de tous les contenus, et donc un hit ratio de 100%.
Mais en fait la mémoire ne coûte pas cher aujourd’hui, et il est souvent possible de effectivement disposer de tous les contenus en mémoire.
Considérons un site qui comporte 10 000 pages, chaque page ayant un poids de 100 ko, pour la partie Html du moins. Ce sont des chiffres assez représentatifs d’un site plutôt haut de gamme. Est-il possible de mettre tout cela en cache ? 10 000 x 100 ko = 1 Go. Un giga-octet de mémoire, c’est très peu de chose aujourd’hui.
Lorsque tout peut tenir en mémoire, il n'est plus question de MRU, on garde tout. Ce qui limitera le hit ratio, malgré tout, c’est la durée de vie des contenus. Si elle est suffisamment longue, par exemple une journée, alors effectivement, chaque contenu ne sera produit qu’une seule fois par jour, puis servi uniquement depuis le cache. Mais si l’on considère que les contenus doivent être rafraîchis toutes les 10 minutes, alors il est possible que certains contenus ne soient jamais resservis depuis le cache.
En supposant la capacité du cache suffisante pour la totalité des contenus, le calcul est assez simple ; il fait intervenir deux paramètres : le nombre de pages servies par seconde et la durée de vie des pages.
Soit D, la durée de vie des pages en cache ; P, le nombre de pages servies par seconde à l’heure de pointe ; N le nombre total de pages.
Faisons l’hypothèse typique que 80% des requêtes portent sur 20% des pages.
Pour que le cache commence à être utile, il faut que sur la durée de vie D, on ait servi plus de pages que N x 0,2, les 20% de pages les plus utilisées.
C’est à dire : P x D x 0,8 > N x 0,2
et donc : D > (N x 0,2) / (P x 0,8)
ou encore D > N / 4P.
Attention, on calcule ici le seuil à partir duquel le cache commencerait à être utile. Ce n’est pas par cette formule qu’il faut calibrer la durée de vie en cache des contenus. Plus la durée de vie est importante, plus le bénéfice du cache est grand. En fait le réglage est d’ordre fonctionnel et non technique : quelle est la durée de validité d’un contenu, quelle est la réactivité requise en cas de changement ?
Application numérique typique : N = 10 000 pages, P = 10 pages par seconde : D = 3 minutes environ. 10 pages par seconde, c’est environ 40 000 visites par jour, c’est donc un site moyen en termes d’audience. Si l’on prend P = 50 pages par seconde, on est au niveau d’un site plus sérieux, à 200 kv/j, et le cache commence à être utile à partir de 0,5 minutes. Le calcul est simplifié, évidemment, car on pourrait s'intéresser aussi aux 10 pages qui représentent peut-être 20% du trafic total. Pour ces pages-ci, on trouverait D > 10 / (P x 0,2 ), c'est à dire que le cache serait utile encore plus tôt.
On en déduit plusieurs choses :
Plus l'audience est forte plus le bénéfice du cache est important, c'est assez évident.
Mais plus important: pour un site à forte audience, même un cache de courte durée de vie commence à être utile, c’est à dire que le bénéfice du cache n’est pas incompatible avec une haute fréquence de mise à jour.
Sites personnalisés et contenus temps-réel
Un bon cache peut servir quelques milliers de pages à la seconde, sur un unique serveur. Et ce n’est pas tout : le cache contribue aussi à la haute disponibilité puisque si l’application ne répond pas, le cache peut toujours fournir une page, peut être un peu ancienne, mais c’est mieux que rien.
Tout cela est magique, du moins tant que l’on sert les mêmes pages à tout le monde, au moins pour quelques temps. C’est le cas de beaucoup de sites, mais depuis quelques années déjà, on observe une tendance puissante vers la personnalisation des sites : chaque internaute est identifié, et reçoit une page spéciale, dépendant de ses préférences et options.
Si chacun reçoit une page unique, c’en est fini du cache et de ses milliers de pages par seconde. Faut-il en rester là ? Soit renoncer à des sites personnalisés, soit empiler une trentaine de serveurs pour tenir la charge ?
Non, heureusement, il nous reste quelques armes.
Introduction au cache par fragments
A bien y regarder, même si chaque page est unique, de nombreux morceaux de la page sont les mêmes pour différents internautes. A défaut de mettre la page entière en cache, on peut donc tenter de mettre des morceaux de pages en cache, des fragments. Typiquement, beaucoup d’éléments de navigation, mais aussi des blocs entiers de contenus, seront intégrés à l’identique dans les pages de tels et tels internautes, même si c’est éventuellement à des emplacements différents.
Cette réflexion amène à la notion de cache par fragments : on ne gère plus en cache des pages web entières mais des fragments de pages. Et le principe est que chaque fragment peut avoir une durée de vie particulière : des parties fixes de la page, un empied par exemple, ne seront rafraîchies que toutes les 4 heures, tandis que des actualités récentes seront rafraîchies tous les ¼ d’heure, et certains fragments, eux, ne seront pas du tout mis en cache, ils seront systématiquement demandés au serveur.
Agrégation de fragments et portails J2EE
La gestion d’un cache par fragment combine donc deux fonctions distinctes : la fonction d’agrégation et la fonction de cache. La fonction d’agrégation consiste à produire une page à partir de différents fragments. C’est une fonction qui correspond à un besoin indépendamment de la problématique du cache, puisque c’est la fonction principale des outils de portail, des outils tels que Jetspeed, Liferay, Uportal, ou encore Websphere Application Portal. Ce sont les portails du monde J2EE, les portails répondant à la norme JSR168. Cette norme définit des APIs entre le portail agrégateur et les applications fournissant les contenus agrégés. Ces APIs sont définies en Java, et donc ne conviennent que pour cet environnement, elles ne sont pas technologiquement neutres. C’est là une limitation intrinsèque des portails J2EE : ils sont faits pour agréger des contenus fournis par des applications Java. Et ce n’est pas tout : des applications Java écrites pour supporter les APIs JSR168 du portail. C’est beaucoup demander et dans la pratique, le besoin est très souvent d’agréger des contenus issus d’applications d’une part hétérogènes, et d’autre part pré-existantes. En d’autres mots : il faut prendre les applications comme elles sont.
Or la fonction du cache agrégateur de fragments reprend, pour partie, cette mission d’agrégation du portail, mais de manière à la fois plus simple et technologiquement neutre.
Edge Side Include
Akamai a défini le Edge Side Include (ESI), devenu une norme du W3C, et adopté par quelques autres grands acteurs. ESI permet d’inclure dans une page web des tags <esi :include src= "http://www.mysite.com/fragment01" />, ce qui signifie qu’à cet endroit de la page doit être inséré le fragment obtenu à l’adresse indiquée.
Dans un système de cache par fragment, le gestionnaire de cache est en charge d’élaborer la page, en insérant tous les fragments à la position appropriée. Lorsque tous les fragments sont en cache, c’est immédiat, et cela ne requiert aucun accès au serveur http. Si un fragment n’est pas dans le cache, ou bien si la version en cache est périmée, trop ancienne, alors ce fragment seulement est obtenu auprès du serveur.
Un tel dispositif de cache par fragments permet donc de faire cohabiter dans les pages, des parties stables et des parties dynamiques, des parties identiques pour tous et des parties personnalisées, chacune bénéficiant d’un cache conforme à son besoin.
L’un des inconvénients du cache par fragment, qu’il s’appuie sur ESI ou non, est qu’il n’est pas transparent pour les applications. La structure même de l’application est impactée : au lieu de servir des pages, elle doit servir des fragments. Ce n’est pas anodin car toutes les applications, tous les outils de développement, sont conçus pour élaborer des pages web. A commencer par les outils de gestion de contenus, qui sont derrière la plupart des sites.
Un cache ESI est donc un bel outil, mais il implique en général de recevoir des fragments élaborés spécifiquement à cet usage. Dans certains cas, on souhaite plutôt manipuler des fragments directement récupérés d’applications existantes, c’est à dire des morceaux extraits de pages entières, typiquement un bloc <div/> au sein de la page.
Quelques mots encore sur Akamai. Outre la spécification ESI et son implémentation, la spécificité de Akamai - comme de ses quelques concurrents "CDN", Content Delivery Networks - est de proposer des boîtiers cache situés non pas juste devant les serveurs, mais juste avant les modems, chez les fournisseurs d’accès à Internet (FAI). C’est ce qui justifie la dénomination « Edge Side Include », le « Edge », c’est le bord de l’Internet, c’est à dire les FAI. A défaut d'être chez tous les FAI, Akamai est du moins présent dans un grand nombre de pays. Ainsi, lorsque le cache possède tous les fragments de la page, la page est servie directement localement, sans avoir à traverser l’Internet, ce qui fait gagner quelques millisecondes, voir quelques dixièmes de secondes pour un accès très éloigné, mais fait gagner aussi de la bande passante auprès de votre hébergeur.
C’est donc une solution intéressante, mais très coûteuse, et le prix est souvent le facteur dissuasif.
Des caches ESI sans CDN ?
Malheureusement, il n’existe pas d’outil open source de gestion d’un cache ESI. C’est une fonctionnalité prévue de longue date dans Squid v3, mais Squid v3 n’est pas « production ready » à ce jour, selon l’aveu même de ses développeurs : « la plupart des bugs restant de Squid v3 concernent le ESI ».
Varnish en est à peu près au même stade, avec une implémentation d’un sous-ensemble de la norme ESI, mais ici aussi, l’avertissement est « attendez-vous à des bugs » !
C’est toutefois une des champs de développement les plus actifs depuis quelques temps, et l’on peut espérer avoir des solutions robustes avant la fin de l’année.
Jahia est un outil portail et CMS qui intègre un cache ESI distinct. Jahia n’est pas à proprement parler un produit open source, mais rend néanmoins disponibles la totalité de ses sources. Le cache ESI de Jahia n’est pas conçu pour être utilisé de manière générique, en dehors d’une plateforme Jahia.
Oracle et IBM ont développé chacun un cache ESI, mais ne l’ont pas rendu disponible en open source.
OSCache
Nous avons évoqué jusqu’ici la gestion en cache de pages Html, ou de fragments de pages. Mais la problématique de cache intervient à bien d’autres niveaux. Chaque fois qu’une application accède à de l’information qui a une certaine durée de validité, et que cet accès est relativement lent, il peut y avoir un bénéfice important à une gestion de cache. C’est le cas typiquement de l’accès à une base de données, mais aussi à un fichier de données, ou encore à un webservice distant. Ainsi, on sait bien que la gestion des résolution DNS s’appuie beaucoup sur une gestion en cache, à différents niveaux.
Dans le monde du développement J2EE, OSCache est l’outil magique de gestion du cache au niveau applicatif. Il permet de gérer en cache n’importe quel objet Java, morceau de page JSP, ou page entière produite par une servlet, et offre une immense flexibilité de configuration et de programmation. OSCache permet en outre de stocker les objets cachés sur disque et donc de retrouver le cache après arrêt-relance du serveur.
Mais OSCache ne gère pas des pages externes au serveur, il ne peut donc pas être utilisé dans un contexte d’agrégation de ressources hétérogènes, provenant de différents serveurs, ou même de différentes applications d’un même serveur.
Server-Side Include plus cache Squid
Pour le site Sport24, Smile a mis en place une architecture de cache à très hautes performances. Sport24 est un site qui doit pouvoir servir jusqu’à 3000 pages par seconde aux heures de pointe. C’est très au delà de ce qu’une application ordinaire peut servir, puisque pour fixer les idées, un outil de gestion de contenu ordinaire sert entre 10 et 200 pages par seconde environ. Bien sûr, on peut empiler les serveurs dans des racks, mais c’est une voie qui devient vite très coûteuse. Ajoutons que le site Sport24 inclut à la fois des pages personnalisées, et une fonctionnalité de suivi d’événements sportifs en temps réel. Toutes choses que les systèmes de cache détestent !
Il a donc fallu trouver des solutions particulières. Notre architecture pour le site Sport24 met en place un cache par fragment. Il n’est pas de type ESI car malheureusement les caches ESI de l’open source ne sont pas encore opérationnels pour un environnement de production. Mais le principe est très comparable. L’agrégation est réalisée au moyen du module Server Side Include (SSI) du serveur Apache. Ce module très standard permet de définir des pages web qui intègrent des balises include, définissant l’insertion d’un fichier dans la page. Ça ressemble donc à de l’ESI, une page étant composée de multiples fragments. La différence est que le SSI ne manipule que des fragments sous forme de fichiers, et non de ressources http distantes. Et que SSI ne gère pas de cache, ce n’est pas son rôle. Néanmoins dans notre cas, nul besoin de cache à ce niveau puisque les fragments sont déjà des fichiers statiques. En revanche, les pages une fois assemblées sont ensuite servies par l’intermédiaire d’un cache Squid, placé en frontal.
Une particularité de cette configuration, par rapport à de l’ESI, est que le cache est regénéré en mode push. C’est le CMS qui pousse les nouveaux fragments, et non le cache qui vient les tirer. On appelle cela parfois un cache actif par opposition à un cache passif, qui fonctionne en mode pull. L’avantage du mode push, c’est que le CMS sait parfaitement ce qui a changé et quand. Tant qu’un fragment n’a pas changé, inutile de le regénérer. Mais chaque contenu élémentaire n’est pas un fragment : un même fragment inclut le plus souvent plusieurs contenus. Typiquement, un pied de page pourra être un fragment unique, mais il porte différents liens, correspondant à différents contenus. Et il suffit que l’un de ces contenus soit modifiés pour forcer à regénérer le fragment. Ce qui implique donc de parfaitement modéliser la dépendance entre les contenus élémentaires et l’ensemble des fragments dans lesquels ils sont publiés, afin de piloter la regénération des fragments statiques.
Le Web Assembling Toolkit
Pour d’autres types de projets, Smile a mis au point le Web Assembling Toolkit . Il s’agit d’un ensemble de taglib JSP, qui permettent de constituer des pages en insérant du contenu obtenu en http à partir d’autres serveurs. Et même si l’agrégation est sa mission première, Web Assemble Tool gère aussi un cache, sur chacun des fragments ainsi obtenus.
Comme on l’a vu, la clé de la haute performance c’est de réunir cache et agrégation. Web Assemble Tool combine ainsi les deux rôles : outil d’agrégation, et outil de cache par fragments.
Et Web Assemble Tool a une autre caractéristique : lorsqu’il appelle un serveur pour obtenir un fragment, il est capable d’extraire un petit morceau de la page obtenu. C’est ce que l’on appelle du web-clipping : on obtient une page, et on découpe le morceau qui nous intéresse, qui constituera le fragment. Cette technique permet en particulier d’obtenir des fragments à partir d’applications existantes, qui n’auraient pas été conçues spécifiquement pour être agrégées ainsi au sein d’un portail. Pour certains, le web-clipping est une solution un peu rustique, du bricolage presque. Ne vaudrait-il pas mieux invoquer un superbe web-service ? C’est discutable. Le bénéfice essentiel du webclipping est le caractère non-structurant : il ne demande rien de particulier aux applications intégrées.
Placé en frontal des applications, Web Assemble Tool joue en quelque sorte le rôle d’un portail. Mais dans certains cas, il est en fait combiné à un véritable portail, par exemple Jahia. C’est le cas du nouveau site d’assurance idmacif.com, de la Macif, ou du portail de Bouygues Immobilier.
Web Assemble Tool est également utilisé par Editions Francis Lefebvre, Bluestar Silicones, et le Conseil Supérieur du Notariat. C’est un projet open source sous licence Apache.
Commentaires