Une extension pour optimiser les performances de son instance StatusNet

Jean Baptiste FAVRE

Juillet 2011

English version will be available soon.

Dans l'épisode précédent

Cette publication constitue le troisième épisode de mon "feuilleton de l'été" consacré à StatusNet. Vous pouvez consulter les précédents là:

Aujourd'hui, nous allons nous concentrer sur les optimisations côté client. Pour cela, nous utiliserons les outils PageSpeed et YSlow. Ce sera également l'occasion de découvrir le système de plugins intégré à StatusNet.

Introduction

De plus en plus, les mesures de performances web prennent de l'importance. Nous avons vu comment optimiser le backend (StatusNet lui-même), mais le frontend (votre navigateur) a également son importance.

Les optimisations côté client ont pour but d'accélérer et fluidifier le chargement des pages. 2 outils permettent de guider le développeur: Yslow et PageSpeed.

Les outils

À tout seigneur tout honneur, nous commencerons par le plus ancien: YSlow, développé par les équipes de Yahoo. Actuellement en version 3.0.3, il est disponible sous forme d'extension Firefox. Il mesure l'application de recommandations que vous pourrez trouver ici: Recommandations Yahoo.

Le second a, quant à lui, été développé par Google: il s'agit de PageSpeed. Il est disponible en version 1.11, lui aussi sous forme d'extension Firefox. Il mesure l'application des recommandations suivantes: Web Performance Best Practices.

L'intérêt de ces outils, outre le fait qu'ils sont disponibles sous forme d'extensions Firefox, est qu'ils ne s'appuient pas sur les même recommandations. Il est donc possible, et conseillé, de jouer sur les 2 tableaux. C'est bien entendu ce que nous allons faire.

Si ce n'est déjà fait, je vous conseille donc d'installer ces outils.

Vous pouvez également utiliser des services en ligne. Personnellement, j'utilise GTmetrix qui me permet de surveiller régulièrement mes sites web et, surtout, de conserver un historique des mesures réalisées. Ce point est particulièrement intéressant pour pouvoir mesurer l'impact de telle ou telle optimisation.

Le cahier des charges

Si vous avez déjà lancé une analyse de votre instance StatusNet, vous avez déjà une petite idée des soucis que nous allons essayer de corriger. En vrac:

J'ajouterai à ces recommandations ma touche personnelle: lorsque vous vous rendez sur une page de profil d'un utilisateur StatusNet, par exemple le mien, vous pouvez voir la liste des abonnés et des abonnements de l'utilisateur, même si vous n'êtes pas vous même authentifié.

C'est sympa, mais dans mon cas, pas très cool: mon instance est hébergée sur un petit serveur. À petit serveur, "petites" ressources. Si je peux éviter de charger les avatars de chacun de mes abonnés/abonnements, mon serveur HTTP sera content. Accessoirement, cela permettra d'appliquer une recommandation supplémentaire: limiter le nombre de requêtes HTTP :)

Le cahier des charges que nous devrons respecter est donc:

Les extensions StatusNet

StatusNet dispose d'un système d'extensions (plugins). Ceci permet d'étendre les fonctionnalités de StatusNet sans pour autant avoir à modifier le code source. Il est même possible de modifier le comportement par défaut de StatusNet, toujours grâce aux extensions.

Nous allons donc naturellement utiliser ces possibilités pour arriver à nos fins.

Une extension déjà intégrée propose une partie de ce que l'on veut faire. Il s'agit de MobileProfile.

Cette extension désactive l'affichage d'une partie de la page, la colonne de droite, lorsque l'on y accède à partir d'un équipement mobile. C'est très exactement ce que nous voulons faire, à un détail près: dans notre cas, le critère sera le status authentifié ou non du visiteur.

En ce qui concerne l'optimisation de l'inclusion des CSS et autres Javascript en revanche, nous n'aurons pas d'autre choix que de modifier le comportement par défaut de l'application: en effet, ces actions sont réalisées au sein de la classe action et, pour autant que j'ai pu voir, aucun plugin ne modifie le comportement par défaut.

Nous allons donc nous en charger.

Les évènements dans StatusNet

Au cœur de StatusNet, tout ou presque est "évènements". L'arrivée d'un message, l'envoi d'un message, tout ceci est géré de manière évènementielle. Bien entendu, il en est de même de la construction et de l'affichage des pages HTML.

Les différents évènements disponibles sont décrits dans le fichier EVENTS à la racine de l'installation StatusNet.

Il est extrêment simple de s'inscrire pour pouvoir gérer un évènement. Par exemple:

StartShowScripts
Cet évènement déclenche l'inclusion du code HTML de chargement des scripts Javascript.

Un plugin peut donc s'inscrire à cet évènement, simplement en définissant une fonction onStartShowScripts. Simple, non ?

L'autre point important à connaitre pour développer un plugin concerne le code retour des gestionnaires d'évènement. La solution retenue par StatusNet est la suivante:

Les actions par défaut, pour autant que j'ai pu m'en apercevoir, sont définies dans la classe lib/action.php.

Nous savons donc maintenant, dans le principe tout au moins, comment "détourner" le flux d'exécution de StatusNet. Nous pouvons donc passer à l'étape suivante.

L'extension WebPerformances

(Vous aurez noté l'originalité du nom de l'extension)

L'idée est donc d'utiliser l'extension WebPerformances pour étendre les fonctionnalités de MobileProfile tout en remplaçant un certain nombre d'actions par défaut de StatusNet.

Commençons par créer le squelette de l'extension. À la racine de l'installation de StatusNet, il faut créer le répertoire de l'extension:

Création de l'extension
mkdir plugins/WebPerformances
cd plugins/WebPerformances
touch WebPerformancesPlugin.php

Le contenu de WebPerformancesPlugin.php est, pour l'instant:

cat WebPerformancesPlugin.php
<?php
/**
 * StatusNet, the distributed open-source microblogging tool
 *
 * FrontEnd Web Performances plugin that uses Mobile Profile Plugin
 *
 * PHP version 5
 *
 * LICENCE: This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program.  If not, see <//www.gnu.org/licenses/>.
 *
 * @category  Plugin
 * @package   StatusNet
 * @author    Jean Baptiste Favre <statusnet@jbfavre.org>
 * @copyright 2011 Jean Baptiste Favre
 * @license   //www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
 * @link      //status.net/
 */

if (!defined('STATUSNET') && !defined('LACONICA')) {
    exit(1);
}

require_once INSTALLDIR.'/plugins/MobileProfile/MobileProfilePlugin.php';

/**
 * Superclass for plugin to output FrontEnd Web Performances plugin
 *
 * @category Plugin
 * @package  StatusNet
 * @author   Jean Baptiste Favre <statusnet@jbfavre.org>
 * @license  //www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
 * @link     //status.net/
 */
class WebPerformancesPlugin extends MobileProfilePlugin
{
    function onPluginVersion(&$versions)
    {
        $versions[] = array('name' => 'WebPerformances',
                            'version' => STATUSNET_VERSION,
                            'author' => 'Jean Baptiste Favre',
                            'homepage' => '//www.jbfavre.org/cv',
                            'rawdescription' =>
                            _m('Description of your plugin'));
        return true;
    }
}

Rien de notable pour le moment. La classe WebPerformancesPlugin étend la classe MobileProfilePlugin. C'est pour cette raison que j'ai ajouté un require PHP juste avant la définition de la classe: il permet de s'assurer que l'extension MobileProfile sera disponible lors de l'initialisation de notre extension WebPerformances.

Puisque notre extension étend l'extension MobileProfile, il est nécessaire que notre classe instancie sa classe parente:

Fonction __construct() dans l'extension WebPerformances
function __construct()
{
  parent::__construct();
}

À partir de maintenant, nous disposons d'ors et déjà de toutes les fonctions de la classe MobileProfilePlugin dans notre classe WebPerformancesPlugin.

L'extension MobileProfile

L'extension MobileProfile va définir une variable serveMobile en fonction du user-agent. À partir de la valeur de cette variable, l'extension va décider d'afficher, ou pas, telle ou telle partie de la page. Les évènements, qui nous intéressent, gérés par MobileProfile sont:

StartShowStatusNetStyles

Showing StatusNet Style links

Dans cette fonction, l'évènement ne sera traité par l'extension que si on est en présence d'un équipement mobile. Dans le cas contraire, le traitement au sein de l'extension est stoppé. Mais le return true laisse la possibilité à une autre extension, ou à StatusNet d'appliquer une action par défaut.

Fonction onStartShowStatusNetStyles dans l'extension MobileProfile
function onStartShowStatusNetStyles($action)
{
	if (!$this->serveMobile) {
		return true;
	}
[...] do stuff [...]
	// Allow other plugins to load their styles.
	Event::handle('EndShowStatusNetStyles', array($action));
	Event::handle('EndShowLaconicaStyles', array($action));

	return false;
}
StartShowHeader

Showing before the header container

Même topo ici, l'évènement ne sera traité que si un équipement mobile a été détecté. Dans le cas contraire, l'extension passe la main à l'action par défaut.

Fonction onStartShowHeader dans l'extension MobileProfile
function onStartShowHeader($action)
{
	if (!$this->serveMobile) {
		return true;
	}
[...] do stuff [...]
	return false;
}
StartShowAside

Showing before the Aside container

Dans le cas où un équipement mobile est détecté, la barre latérale droite est masquée. L'effet est obtenu grâce au return false qui interrompt tout traitement de cet évènement.

Fonction StartShowAside dans l'extension MobileProfile
function onStartShowAside($action)
{
	if ($this->serveMobile) {
		return false;
	}
}

Améliorer l'extension MobileProfile

Maintenant que nous avons ciblé les fonctions à surcharger, il est temps de mettre les doigts dans le code. Je ne mettrai pas le code ici pour éviter d'avoir à mettre à jour le document à chaque nouvelle version. Vous pouvez télécharger l'extension WebPerformances et examiner le code à votre aise.

StartShowHeader

Dans notre cas de figure, nous allons surcharger cette fonction pour simplifier l'affichage si l'utilisateur n'est pas authentifié.

Notez encore une fois le return false. Cela signifie que le traitement de l'évènement StartShowHeader sera stoppé après cette extension. Notez également que, dans le cas d'un utilisateur authentifié, l'extension WebPerformances passe la main à MobileProfile sans faire quoique ce soit.

Vous remarquerez au passage que l'appel à la fonction _showLogo a été supprimé. En effet, j'ai décidé de ne pas afficher de logo aux utilisateurs non authentifié (et oui, je fais ce que je veux avec mon code :p ).

StartShowAside

Là encore, si l'utilisateur est authentifié, nous passons la main à l'extension MobileProfile dont le comportement par défaut nous convient parfaitement.

Et c'est tout pour l'amélioration de l'extension MobileProfile. Passons à la suite, la modification du comportement par défaut de l'application.

Remplacer certaines actions par défaut

Nous cherchons ici à regrouper les feuilles de style et les scripts Javascript, pour limiter les requêtes HTTP nécessaires à leur chargement.

En ce qui concerne les feuilles de style, tout se passe dans la fonction onStartShowStatusNetStyles. Mais cette fonction est déjà surchargée par l'extension MobileProfile. Qu'à cela ne tienne, nous allons la détourner nous aussi.

StartShowStatusNetStyles

Dans notre cas, nous voudrons bien entendu conserver le comportement de l'extension MobileProfile qui affiche les feuilles de style adaptées au équipements mobiles, tout en remplaçant l'appel à $action->primaryCSSLink(). Cette fonction, définie dans le fichier lib/action.php va insérer les feuilles de styles par défaut. Problème, il y en a 2 et nous n'en voulons qu'une.

De plus, dans le cas où nous sommes en présence d'un utilisateur non authentifié et n'utilisant pas un équipement mobile, nous cacherons malgré tout la barre latérale. Dans ce cas, il faut aussi surcharger la feuille de style de manière à ce que la colonne de gauche occupe alors tout l'espace disponible. Compte tenu de la taille du style en question, il sera inséré directement dans la page. La fonction devient alors:

Ici, je n'ai pas voulu intégrer les feuilles de style des plugins. Il faut en effet fusionner les feuilles de styles entre elles et le nombre de combinaisons possibles en fonction des plugins utilisés est juste rédhibitoire.

Veuillez vous référer au fichier README fourni avec le plugin pour savoir comment mettre en place le système.

StartShowJQueryScripts

Comme son nom l'indique, cette fonction est chargée d'insérer le code HTML pour charger les scripts JQuery. Par défaut, il y en a 4, plus un cinquième qui, sans être un script JQuery, peut toutefois être intégré avec les autres. La fonction devient:

Bien entendu, comme pour les feuilles de styles, il faut rassembler tous ces fichiers en 1 seul

StartShowStatusNetScripts

Cet évènement n'est pas géré par le plugin MobileProfile. Dans ce cas-ci, nous allons remplacer le comportement par défaut de StatusNet, défini dans le fichier lib/action.php.

Plus précisément, l'affichage du code HTML de chargement du script js/util.min.js est géré dans la fonction showScripts

Ce que nous voulons obtenir est simple: ne plus afficher le code HTML de chargement du script js/util.min.js dans la mesure où nous venons de l'intégrer à js/wp-jquery_util.min.js. Il nous faut donc surcharger l'évènement StartShowStatusNetScripts dans notre extension. Bien entendu, la fonction retournera lavaleur false de manière à stopper le traitement de l'évènement.

Conclusion

Voilà, nous avons vu comment étendre et modifier le comportement de StatusNet à partir d'un exemple, certes simple, mais représentatif de ce que l'on peut faire.

Sources et références

StatusNet

StatusNet
  • //status.net/open-source/
  • //status.net/wiki/
Plugins
  • //status.net/wiki/Plugins
  • //status.net/wiki/HOWTO_Make_a_Plugin

À propos de Jean Baptiste FAVRE

Je passe le plus clair de mon temps libre sur Internet à travailler sur GNU/Linux avec Debian ou CentOS, la virtualisation avec Xen et KVM et les technologies cluster avec Corosync et OpenAIS. Particulièrement intéressé par Linux, Netfilter, la virtualisation, le monitoring et les clusters, la plupart de mes travaux personnels sont publiés sur ce site et les autres ne sauraient tarder.
A titre professionnel, j'administre des serveurs sous RedHat ou CentOS ainsi qu'une ferme de serveurs VMware ESXi version 4.
De temps, à autre, je parviens à lâcher mon clavier pour lire un bon bouquin tout en écoutant de la musique, mais ça ne dure jamais longtemps.

License

Creative Commons License Cette publication est publiée sous contrat Creative Common by-nc-sa

Valid XHTML 1.0 Strict |  Valid CSS |  contrat Creative Common by-nc-sa

Table des matières

  1. Dans l'épisode précédent
  2. Introduction
  3. Les outils
  4. Le cahier des charges
  5. Les extensions StatusNet
  6. Les évènements dans StatusNet
  7. L'extension WebPerformances
  8. L'extension MobileProfile
  9. Améliorer l'extension MobileProfile
  10. Remplacer certaines actions par défaut
  11. Conclusion
  12. Sources and references
  13. About ...
  14. License