Optimisation des applications web : gestion du cache

Il y a un certain nombre de mauvaises pratiques de développement d’une application web sur lesquelles on peut travailler pour améliorer l’expérience utilisateur dans une proportion qui peut être parfois très importante.

La première de ces mauvaises pratiques consiste à ne pas spécifier les directives de cache qui indiquent au navigateur de charger les ressources précédemment téléchargées à partir du disque local plutôt que sur le réseau.

Rappelons qu’une page web est souvent constituée de plusieurs dizaines voire centaines de ressources. La page peut contenir des scripts, des images, des feuilles de style, etc. Le chargement de chacune des ressources donne lieu à une requête http. Lorsque toutes les ressources d’une page sont chargées par le navigateur, la page est considérée comme chargée.

Il est encore fréquent que les sites web n’utilisent pas de directive de mise en cache des ressources statiques du type image, librairie javascript, ou encore feuille de style. En l’absence de directive les navigateurs du marché appliquent des stratégies différentes. Certains utilisent des heuristiques pour déterminer combien de temps une ressource doit être maintenue dans le cache du navigateur, d’autres imposent que les directives de cache soient définies dans les entêtes http pour aller chercher une ressource à partir du cache. Selon la version du navigateur utilisé cela peut se traduire par une multitude de requêtes http inutiles qui contribuent à dégrader les temps de réponse observés par l’utilisateur.

Par conséquent, pour homogénéiser l’utilisation du cache quel que soit le navigateur de l’utilisateur, il est recommandé de configurer explicitement les directives de cache.

Il m’est arrivé d’observer chez un prestataire de service infogérant une application pour le compte d’un client, une architecture technique ou les ressources statiques étaient servies par une ferme de serveurs d’application déployée derrière un load balanceur. Sans directive de cache, l’accès à ces ressources http donnait lieu à une sollicitation incessante de cette couche et consommait inutilement des ressources de l’infrastructure (bande passante, cpu, mémoire, etc). Outre ces problématiques de consommation de ressources mutualisées entre différents clients, l’application présentait également des temps de réponse très longs. Certes ces temps de réponse n’étaient pas dus uniquement à la problématique de cache, mais elle y contribuait dans une proportion qu’il est difficile d’estimer.

Parfois les directives de cache ne sont pas utilisées correctement ce qui conduit également à observer les mêmes effets que ceux décrits précédemment. Par exemple la directive cache-control possède un attribut max-age qui spécifie la durée en seconde pendant laquelle la ressource est conservée dans le cache du navigateur. Il arrive que l’attribut max-age soit positionné avec une valeur trop faible ce qui conduit à de fréquentes invalidations des ressources du cache. Le même comportement peut être observé lorsque la date d’expiration  spécifiée par la directive « expires » est fixée dans le passé.

Deux techniques doivent être privilégiées pour activer l’utilisation du cache lors de l’accès à certaines ressources.

  • Utilisation de la directive http « expires »
    Cette directive permet de spécifier une date d’expiration de la ressource dans le cache. Jusqu’à ce que cette date soit atteinte le navigateur conserve la ressource dans le cache  et n’interroge plus le serveur pour obtenir cette ressource. Lorsque la date d’expiration est atteinte le navigateur lance une requête conditionnelle pour vérifier s’il existe une nouvelle version de la ressource ;
  • Utilisation de la directive http « cache-control »
    L’attribut max-age force le navigateur à conserver une ressource dans le cache pendant une durée exprimée en seconde depuis la requête originale. Lorsque le délai est dépassé le navigateur lance une requête conditionnelle pour vérifier s’il existe une nouvelle version de la ressource ;

Il existe également une technique basée sur la vérification d’une empreinte de la ressource appelée « ETags». Les ETags sont utilisés pour pouvoir différencier deux versions d’un même document. C’est un identifiant de la forme « 58a3d-67a-4b00bd2a4ff00 », unique pour une URL. Cette empreinte est créée par le serveur puis est transmise dans les entêtes HTTP lors de la communication entre le navigateur et le serveur via l’entête If-None-Match: « 58a3d-67a-4b00bd2a4ff00 ». Si la ressource n’a pas été modifiée, le serveur répond avec un code http 304 – Not modified » indiquant que la ressource peut être chargée à partir du cache du navigateur. Dans le cas contraire la ressource est renvoyée au navigateur.

Par défaut les serveurs Apache génèrent l’Etag à partir de l’inode « identifiant d’un fichier », la taille du fichier, et la date de dernière modification du fichier. Cette construction de l’empreinte peut parfois être problématique lorsque l’infrastructure repose sur une ferme de serveurs. Dans ce cas l’Etag d’une ressource est différent sur chaque serveur. Cela peut donc conduire à des effets de bord inattendus.

Notons que la mise en place des directives de cache sur les ressources statiques permet d’améliorer l’expérience utilisateur pour les utilisateurs ayant déjà accédé au site. Pour ceux qui visitent le site web pour la première fois cette technique n’a aucun impact sur les temps de réponse des pages. Il existe heureusement d’autres techniques qui permettent d’améliorer l’expérience utilisateur pour ces utilisateurs.

Pour illustrer ces propos, j’ai capturé des informations concernant la page d’accueil d’un site de jeu en ligne pour lequel JavaPerf Consulting est intervenu lors d’un audit de performance. Il semble que cette société n’ait pas appliqué toutes nos recommandations.

L’analyse du contenu est fournie selon trois axes :

  • Le nombre total d’octets téléchargé lors de l’accès à la page ;
  • Le taux d’utilisation du cache du navigateur ;
  • La quantité d’informations rechargée à chaque visite de l’internaute ;

La page d’accueil du site charge environ 1037 Ko et moins de 1% des ressources sont mises explicitement en cache. Par conséquent, lors d’une prochaine visite de l’internaute, le navigateur rechargera entièrement le contenu de la page ce qui donnera lieu à l’exécution de 112 requêtes http (dont la plupart du contenu est constitué d’images, de feuilles de style, et de librairies javascript).

Sur ce site, l’expérience utilisateur n’est loin d’être optimale. Le temps de chargement de la page d’accueil lors de la première visite de l’internaute est de plus de 11 secondes (mesure prise à partir d’un navigateur IE 8 situé à Paris). Le rendu de la page commence après 8 secondes ce qui signifie que pendant 8 secondes l’internaute attend une réponse devant une page blanche. Lors de la deuxième visite, la page est affichée en environ 6 secondes et le rendu de la page commence après 4,5 secondes.

Les administrateurs du site ont mis en place la technique des ETags décrites plus haut. On peut observer des effets de bord liés à la codification des URLs, et au chargement des librairies javascript qui pénalisent les temps de réponse obtenus lors d’une nouvelle visite du site… Nous reviendrons sur cette problématique dans un prochain billet.

speed of the web - analyse du contenu d'une page

Publicités

A propos jlerbsc

founder of JavaPerf Consulting Http://www.jperf.com
Cet article a été publié dans performance. Ajoutez ce permalien à vos favoris.

Laisser un commentaire

Entrez vos coordonnées ci-dessous ou cliquez sur une icône pour vous connecter:

Logo WordPress.com

Vous commentez à l'aide de votre compte WordPress.com. Déconnexion / Changer )

Image Twitter

Vous commentez à l'aide de votre compte Twitter. Déconnexion / Changer )

Photo Facebook

Vous commentez à l'aide de votre compte Facebook. Déconnexion / Changer )

Photo Google+

Vous commentez à l'aide de votre compte Google+. Déconnexion / Changer )

Connexion à %s