Tout d'abord rappelons un peu le mécanisme de base de l'affichage d'une page web !

Lorsque vous saisissez une url dans votre navigateur, vous allez envoyer  une requête au serveur où le site est hébergé afin qu’il réunisse toutes les informations utiles à l’affichage de la page demandée. Généralement ces informations sont dynamiques, stockées dans une base de données par exemple, et les regrouper demandes un certain délai.

De plus ces opérations vont être répétées car cette génération va devoir s'effectuer pour chaque visiteur ! Vous comprendrez donc que plus le nombre de connexions simultanées est grand, plus les ressources serveurs requises vont être conséquentes.

La mise en cache de vos pages va vous permettre de gagner un temps précieux. Il existe plusieurs solutions afin de mettre en cache des données de votre page. Voici quelques solutions :

  • La mise en cache de page directement sur votre application. La solution est dépendante de votre application et ne peut pas être déportée. La plupart des CMS vont proposer ce type de solution.
  • La mise en cache du navigateur : Votre navigateur peut garder en mémoire certains éléments de la page, et notamment à votre demande au travers de certains paramétrages de votre serveur web.
  • La mise en cache d’objet : La plupart des sites dynamiques vont utiliser une base de données, le cache objet va garder en mémoire certaines informations afin de les retourner plus rapidement.
  • Les CDN : L'utilisation d'un CDN va vous permettre de mettre en cache le HTML et/ou les fichiers statiques (les images, le CSS et le JS par exemple).

Mais pourquoi autant de solutions pour faire diminuer le temps d'accès à mes pages ? Vraiment utile ?

Plus vite, toujours plus vite ... ⚡

Le délai d'accès à vos pages est un élément technique clé d'un référencement SEO réussi notamment auprès du moteur de recherche numéro un dans le monde : Google.

D'après Google, le délai entre l'utilisateur qui effectue une requête HTTP(S) sur votre serveur et le premier octet de la page reçue par le navigateur du client, ne devrait pas dépasser les 200 ms :

https://developers.google.com/speed/docs/insights/Server?hl=fr

Ce délai est appelé :  Le Time to First Byte (TTFB). Il est donc généralement utilisé pour mesurer la réactivité d'une application web.

Bien sûr, techniquement votre serveur Web n'est pas l'unique intermédiaire entre une première requête sur votre site et votre utilisateur. On pourra citer au moins la requête DNS ! 😂

🚩 Insights ne stipule dans la dernière version de sa documentation qu'une valeur rédhibitoire de 600ms. Je n'ai pas retrouvé mention d'une valeur "souhaitable" ! Mais les experts web marketing parlent toujours de 200ms 🚩

Comment l'infrastructure, au service de mon application, peut - et doit - faire baisser cette valeur ?

Varnish 🧴

Dans cet article, nous allons voir ensemble un système de cache bien précis et que je n'ai pas cité plus haut. L'utilisation d'un reverse proxy, et plus précisément Varnish.

Tout d'abord Varnish c'est quoi ?

Varnish est un accélérateur d'applications Web. Installé en amont de vos applications qui diffusent du contenu HTTP, il va mettre en cache le contenu de vos pages afin de les distribuer bien plus rapidement que votre serveur Web. Voici comment le site officiel décrit l'application :

Varnish Cache is really, really fast. It typically speeds up delivery with a factor of 300 - 1000x, depending on your architecture. A high level overview of what Varnish does can be seen in this video.

Si vous n'êtes pas encore convaincu, ou que vous souhaitez simplement passer à la technique, attaquons immédiatement avec un exemple !

Go ! 💪

Bien évidement, je ne vais pas utiliser une application sans passer par un ... conteneur et donc Docker !

Il y a encore quelques mois, aucune image officielle n'était présente sur le Hub Docker. Mais cette image est maintenant présente :

https://hub.docker.com/_/varnish

Je vais donc l'utiliser dans ma stack afin de mettre en cache mes pages.

Pour cet exemple, je vais utiliser une stack relativement simple afin de nous attarder sur la première mise en place.

Je reviendrais dans un second article avec une mise en situation plus concrète !

Une requête arrivant sur mon site va donc suivre ce cheminement :

Utilisateur -> Varnish -> Nginx
  • Varnish sera ici mon point d'entrée.
  • Nginx diffusera sa page par défaut.

Avant la mise en place de mon service Varnish, mon fichier docker-compose ressemble à ça :

version: '3.7'
services:
  my-site:
    image: nginx
    restart: unless-stopped
    networks:
      - lan

networks:
  lan:

Je vais donc rajouter un service Varnish à cette stack existante !

Les règles de fonctionnement du cache sont établies dans des fichiers de  configuration utilisant le langage de script VCL. Celui-ci va vous permettre de décider d'éventuelles exclusions à votre cache. Mais dans un premier temps, mettons l'intégralité de notre site en cache.

Je vais créer un fichier default.vcl avec les instructions suivantes :

vcl 4.0;
backend default {
  .host = "my-site";
  .port = "80";
}

sub vcl_deliver {
  # Display hit/miss info
  if (obj.hits > 0) {
    set resp.http.V-Cache = "HIT";
  }
  else {
    set resp.http.V-Cache = "MISS";
  }
}

Varnish va donc se connecter à mon service nginx afin de récupérer les pages HTTP. Je vais ajouter un header afin de valider le fonctionnement ( ou non ) de mon cache !

Je vais maintenant ajouter ce service à ma stack :

  varnish:
    image: varnish
    restart: unless-stopped
    volumes:
      - ./default.vcl:/etc/varnish/default.vcl:ro
    tmpfs: /var/lib/varnish:exec
    networks:
      - lan
    ports:
      - 80:80

Ce n'est donc plus Nginx qui va se charger des requêtes mais bien Varnish !

À noter l'utilisation de tmpfs !

Il ne me reste alors plus qu'à vérifier le bon fonctionnement :

 $ curl -s -v --output /dev/null http://127.0.0.1
* Expire in 0 ms for 6 (transfer 0x55ad54496f50)
*   Trying 127.0.0.1...
* TCP_NODELAY set
* Expire in 200 ms for 4 (transfer 0x55ad54496f50)
* Connected to 127.0.0.1 (127.0.0.1) port 80 (#0)
> GET / HTTP/1.1
> Host: 127.0.0.1
> User-Agent: curl/7.64.0
> Accept: */*
>
< HTTP/1.1 200 OK
< Server: nginx/1.17.10
< Date: Tue, 05 May 2020 16:00:53 GMT
< Content-Type: text/html
< Content-Length: 612
< Last-Modified: Tue, 14 Apr 2020 14:19:26 GMT
< ETag: "5e95c66e-264"
< X-Varnish: 2
< Age: 0
< Via: 1.1 varnish (Varnish/6.4)
< V-Cache: MISS
< Accept-Ranges: bytes
< Connection: keep-alive

On peut remarquer qu'avec cette première connexion, le cache n'est pas disponible puisqu'il n'a jamais servi cette page.

Par contre si je recommence :

$ curl -s -v --output /dev/null http://127.0.0.1
* Expire in 0 ms for 6 (transfer 0x55bb210faf50)
*   Trying 127.0.0.1...
* TCP_NODELAY set
* Expire in 200 ms for 4 (transfer 0x55bb210faf50)
* Connected to 127.0.0.1 (127.0.0.1) port 80 (#0)
> GET / HTTP/1.1
> Host: 127.0.0.1
> User-Agent: curl/7.64.0
> Accept: */*
>
< HTTP/1.1 200 OK
< Server: nginx/1.17.10
< Date: Tue, 05 May 2020 16:00:53 GMT
< Content-Type: text/html
< Content-Length: 612
< Last-Modified: Tue, 14 Apr 2020 14:19:26 GMT
< ETag: "5e95c66e-264"
< X-Varnish: 5 3
< Age: 3
< Via: 1.1 varnish (Varnish/6.4)
< V-Cache: HIT
< Accept-Ranges: bytes
< Connection: keep-alive
<
{ [612 bytes data]
* Connection #0 to host 127.0.0.1 left intact

Ma page est donc maintenant en cache !


Nous avons donc pu voir ensemble comment mettre en place un cache varnish afin d'accélérer ses pages webs.

Il faut bien être honnête, mon exemple basique ne vous permet pas encore de mesurer pleinement l'efficacité de ce système. Mais j'ai trouvé cette approche nécessaire afin de faciliter la compréhension de la mise en œuvre.

J'aurais l'occasion dans un prochain article de vous montrer comment allez plus loin avec votre cache, avec un exemple très concret. Vous pourrez alors voir toute la puissance de cet outil, mais ce sera également l'occasion d'en découvrir la complexité !

En tout cas  n'hésitez pas à m'apporter des remarques ou des commentaires sur Twitter ! C'est toujours un plaisir d'avoir des retours ! 😘