ACCUEIL > TUTORIAUX SPIP

PARALLÉLISATION DES CHARGEMENTS AVEC SPIP

   

Ces derniers temps, je constatais de lourds ralentissements sur mon site Flip-Zone. La charge processeur était loin d’atteindre le maximum, et la bande passante n’était pas saturée non plus. Solution : la configuration par défaut d’Apache de la Debian est excessivement sous-dimensionnée ; en augmentant le MaxServers et le ServerLimit à nettement plus que 256, le serveur s’est remis à donner des résultats satisfaisants.

Toujours est-il que, cherchant à améliorer la réactivité du site, je me suis tourné vers l’outil PageSpeed pour Firefox. Quelque soit votre niveau, en tant que webmestre, je vous suggère de l’installer et de tester vos sites. L’outil fournit non seulement les tests, mais aussi de très intéressants conseils d’optimisation de votre site. Il y a de plus des tutoriaux pour tous niveaux sur le site du plugin.

Un point en particulier m’a intéressé : la mise en parallèle des téléchargements des éléments appelés par la page : images, feuilles de style, javascript. Le principe est que le protocole HTTP n’autorise qu’un nombre très réduit de téléchargements simultanés de fichier sur un même domaine (sans doute deux chargements). Quand la page contient beaucoup d’appels à des fichiers externes, tels que des images, le navigateur va charger ces fichiers les uns après les autres (deux par deux). On voit ainsi se charger les images les unes après les autres, de manière séquentielle. Or, avec une connexion rapide, on n’exploite pas la totalité de la bande passante disponible. Il y aurait donc moyen de charger ces images plus rapidement, en lançant plus de chargements en même temps (en parallèle).

La méthode pour réaliser cela est de fournir les images sur plusieurs adresses (noms de domaines) différents. En effet, si le navigateur ne peut charger que deux fichiers en même temps pour un nom de domaine, il peut lancer en même temps les chargements sur plusieurs domaines. C’est expliqué, par exemple, sur cette page du Yahoo User Interface Blog.

Je vous livre comment j’ai procédé pour mon site sous SPIP. Il faut, évidemment, disposer d’une machine dédiée sur laquelle on peut soit-même gérer des sous-noms de domaine, ainsi que la gestion des zones de son nom de domaine au niveau du registrat.

Le principe consiste à créer autant de sous-noms de domaine que nécessaire (pour mes essais actuels : quatre sous-noms de domaine), et à les faire pointer vers le même dossier du serveur (le dossier qui contient déjà le site sous SPIP). De cette façon, l’image qui se trouve habituellement à l’adresse :

est également accessible via les adresses :

Il s’agit, physiquement sur le serveur, du même fichier (fabriqué par les filtres graphiques de SPIP). En revanche, pour le visiteur, ce sont bien des domaines différents, ce qui permet de dépasser la limite de téléchargements simultanés. Le navigateur ne peut lancer simultanément que le chargement de deux images sur www.flip-zone.com, mais il peut lancer en même temps deux chargements sur chacun des sous-domaines. Je peux donc démarrer le chargement, ici, de huit images en même temps sur mes sous-noms de domaine.

Au niveau de la gestion du nom de domaine, il faut évidemment que je fasse pointer tous les sous-noms de domaine vers le même serveur. Plutôt que de me contenter d’une zone du style :

je fais pointer « tout » vers le serveur, grâce à l’astérisque :

Avec cette configuration, tous les sous-noms de domaine de flip-zone.com pointent vers le même serveur. C’est pratique, dans l’absolu, pour se créer des domaines à la demande (dev.flip-zone.com, test.flip-zone.com...), et pour le cas qui m’intéresse, faire directement pointer img0.flip-zone.com à img3.flip-zone.com vers le serveur.

Au niveau du serveur, il faut évidemment indiquer à Apache vers quel dossier du serveur faire pointer ces nouveaux sous-noms de domaine. C’est simple : vers le même dossier déjà utilisé pour www.flip-zone.com.

J’avais déjà une définition complète (avec la gestion des droits) pour www.flip-zone, pointant notamment vers :

  1. <VirtualHost www.flip-zone.com:80>
  2.         DocumentRoot /var/www/flip/zone/
  3.         ...
  4. </VirtualHost>

Je modifie ce VirtualHost en indiquant qu’il sert d’alias à certains sous-domaines (merci à Fred pour le truc) :

  1. <VirtualHost www.flip-zone.com:80>
  2.         DocumentRoot /var/www/flip/zone/
  3.         ServerAlias img*.flip-zone.com
  4.         ...
  5. </VirtualHost>

Je redémarre Apache, et voilà. Pour tester, je me rends sur mon site, j’affiche une image (qui est pour l’instant en http://www.flip-zone.com/..., et je remplace son URL par http://img0.flip-zone.com/.... Si l’image se charge à nouveau, c’est que la configuration est correcte.

Il faut maintenant que j’intervienne dans SPIP pour que mes pages lient les images non plus en relatif (donc vers www.flip-zone.com), mais avec des URL absolues réparties sur les quatre sous-noms de domaine.

Je vous livre ma méthode, très simple et qui ne demande pas beaucoup de modifications des squelettes.

D’abord, je vais faire passer le résultat des pages, en fin de compilation, par un filtre. J’insère dans chaque squelette concerné (sommaire.html, rubrique.html, article.html...) le code suivant :

Cette fonctionnalité de SPIP indique que l’intégralité du squelette, une fois compilé, doit passer par le filtre |filtrer_images_page. Elle s’applique avant la sauvegarde en cache, le résultat est donc bien géré par le cache de SPIP.

Il me faut définir cette fonction PHP. Je l’installe par exemple dans /config/mes_options.php, de façon à pouvoir en profiter dans tous mes jeux de squelettes (puisque j’ai des squelettes spécifiques pour iPad et iPod sur ce site).

La fonction est un peu rustique et on pourra sans doute aisément rendre son écriture plus élégante. Mais elle fonctionne, et j’ai besoin pour mes tests qu’elle soit simple à lire et à modifier (merci cependant à Marcimat pour le morceau de code qui rend ça beaucoup plus propre).

  1. function filtrer_rediriger_images($reg) {
  2.         $lien = $reg[1];
  3.        
  4.         if ( ! preg_match(",^http,", $lien)) {
  5.                 $code = substr(md5($lien), 0, 1);
  6.                 $code = hexdec($code) % 4;             
  7.                 $lien = "http://img$code.flip-zone.com/$lien";
  8.         }
  9.         return " src=\"$lien\"" ;
  10. }
  11. function filtrer_images_page($flux) {
  12.         $flux = preg_replace_callback(",[[:space:]]src=[\"\']([^\"\']*)[\"\'],", "filtrer_rediriger_images", $flux );
  13.         return $flux;
  14. }

La fonction extrait toutes les chaînes du style :

C’est peu précis, mais ça ne semble pas provoquer de dégâts par ailleurs. Cela me permet de récupérer, en une passe, les liens vers des images et vers des fichiers Javascript. On peut facilement la rendre plus précise, ou lui faire également traiter les appels aux feuilles de style.

Pour ma part, j’ai tout de même un dommage collatéral sur l’appel des Javascript externes. J’ai donc modifié le script ainsi (ligne 4) pour les exclure :

  1.         if ( ! preg_match(",^http,", $lien) && !preg_match(",\.js$,", $lien)) {

Pour chaque lien ainsi récupéré, j’exclus les liens qui sont déjà en URL absolue. J’effectue un hachage md5 du lien, ce qui me donne une chaîne en hexadécimal, et je récupère le premier caractère (de 0 à 9 et de a à f). À partir de ce caractère, de manière systématique, je fabrique une valeur entre 0 et 3. Et je retourne le lien en URL absolue vers un des sous-noms de domaine en img0.flip-zone.com à img3.flip-zone.com.

La fonction ne cherche pas à répartir équitablement les liens sur les quatre sous-noms de domaine, mais applique un filtre systématique en fonction de l’URL du fichier (de toute façon, j’ai tellement d’images dans chaque page que la répartition est statistiquement équitable). C’est ce qui garantit que, d’une page à l’autre du site, une image sera toujours sur le même sous-nom de domaine. Sinon, je provoquerais des rechargements d’une image déjà chargée, parce que son URL serait sur un sous-nom de domaine différent.

Et voilà. Au recalcul de la page, les appels vers les images et les fichiers Javascript sont répartis sur les quatre sous-noms de domaine, et j’ai donc le chargement simultané d’au moins 8 fichiers par le navigateur.

À noter.

— La fonction #FILTRE ne concerne que le contenu de la page compilée. S’il y a des inclusions, il faut considérer que :

  • les inclusions statiques en [(#INCLURE...)] sont traitées (puisque le contenu est inséré dans la page avant le passage à #FILTRE ;
  • les inclusions dynamiques en <INCLURE...> ne sont pas traitées. Si l’on veut traiter ces éléments, il faut ajouter #FILTRE à l’intérieur même de ces morceaux de squelettes. Mais c’est aussi une façon d’exclure certaines parties du site.

— Les appels à Google Analytics tels que fournis par Google sont corrompus par le script. Les appels au serveur de publicité OpenX également. Il suffit de placer ces éléments dans des squelettes séparés sur lesquels on ne fait pas passer le filtre (donc appelés par <INCLURE...>).

— Il est déconseillé de trop multiplier les sous-noms de domaine (mon script peut facilement gérer 16 sous-noms de domaine) : il faut en effet que le navigateur interroge le DNS pour chacun des sous-noms de domaine, et on perdrait trop temps avec trop de domaines différents.

— Au niveau du serveur Apache, penser à activer le fonctionnement KeepAlive, et à avoir un MaxServers suffisant (puisqu’on lance plus de connexions simultanément à chaque page).

— Comme préconisé par PageSpeed, j’ai également activé les modules expires et headers, pour que le serveur envoie automatiquement les indications de mise en cache pour les images, les fichiers CSS et les fichiers javascript. Une fois installés, j’ai ajouté la configuration suivante :

— Enfin, je répète ce que j’ai déjà indiqué dans d’autres billets : la gestion/configuration d’un serveur n’est vraiment pas mon domaine. Je bidouille. Je suis donc très preneur de conseils et commentaires de la part d’informaticiens calés sur le sujet pour améliorer cette technique.


modération à priori

Ce forum est modéré à priori : votre contribution n'apparaîtra qu'après avoir été validée par un administrateur du site.

Un message, un commentaire ?
  • (Pour créer des paragraphes, laissez simplement des lignes vides.)

Qui êtes-vous ? (optionnel)