Guide d'introduction aux performances applicatives web

Comment rendre votre site ou application web plus performant.

Plus de 15 entreprises font confiance à DASIO pour leur systèmes d'information

Rejoignez-nous et passez à la vitesse supérieure.

Vous avez un site web, un commerce électronique ou une application web et son temps de chargement n'est pas acceptable ?

Plusieurs éléments peuvent en être la cause, autant du côté applicatif que du service de distribution.

En général, on peut considérer un temps de réponse du serveur comme décent lorsqu'il est en dessous de 1000 millisecondes. Autrement, des améliorations devraient être faites pour de nombreuses raisons. Une application web rapide comporte de nombreux avantages, pour n'en citer que quelques uns :

  • Meilleur taux de rebond et de conversion

  • Moins vulnérable aux attaques de denial of service

  • Moins vulnérable de tomber down lorsque vous êtes l'heureuse victime de l'effet slashdot

  • Peut accueillir un plus grand trafic soudain

  • Vous fait économiser d'importante sommes en matériel / abonnement de serveur

  • Service d'avantage écoénergétique


Logo de Démo
Logo de Démo
Logo de Démo
Logo de Démo
Logo de Démo
Logo de Démo
Logo de Démo
Logo de Démo

Configuration du serveur

Lorsque vous passez votre application en production, vous devez prendre en compte certains points cruciaux pour accélérer au maximum la vitesse de distribution, certains points sont optionnels, et d'autres à ne absolument pas négliger. Un premier qui n'a pas de section dédié, car est d'une totale évidence tout de même certains négligent ou oublient : Activer le paramètre "mode production" / désactiver le paramètre "mode développement".

 Le serveur applicatif doit être prefork

Avec Apache ou Nginx, il est possible de distribuer une page web dynamique directement en chargeant l'application individuellement à chaque requête, et ce sans passer par un serveur applicatif de type prefork, ralentissant considérablement la vitesse de distribution.

C'est pour cela qu'il existe des serveur applicatif prefork, c'est à dire que le serveur va précharger un nombre X (configurable) de processus de votre application en prévision de recevoir des requêtes.

Le fait que l'interpréteur du code sois déjà chargé et opérationnel lorsqu'un client demande une page peut réduire le temps de chargement jusqu'à 10x.

De nombreux serveur de type prefork sont disponibles, notamment uWSGI, PHP-FPM, Puma, Gunicorn et bien plus.

 Le serveur applicatif doit être correctement "tuné"

Il est primordial d'avoir un serveur applicatif, mais tout autant que ce dernier sois bien configuré. De nombreux paramètres s'offrent à vous, tout dépendant des performances de votre serveur ou de l'achalandage sur ce dernier.

Par exemple il y a le nombre de workers, ou process (tout dépendant du serveur). Un worker est une instance de l'application à part entière. Le nombre de workers doit être égal au nombre de cœurs de votre CPU, mais cela peut varier.

Pour chacun des workers, il y aura plusieurs threads. Par exemple, lorsque tous les workers sont au travail et qu'un client fait une requête, la requête sera assignée à un worker; si un thread de ce worker est en attente qu'une ressource se libère (ex: lock d'accès à la mémoire etc..), la requête sera exécutée sur un nouveau thread du même worker.

Généralement 2 threads par workers est une bonne configuration, mais vous pouvez en ajouter plus selon la puissance de votre serveur. Vous pouvez déterminer le nombre parfait de threads à l'aide d'un stress test.

Si vous avez trop de workers, votre serveur risque de littéralement "freezer" au lancement à cause de trop d'utilisation de mémoire ou de CPU pour ce qui est disponible. Si vous en avez pas suffisamment selon le nombre de requêtes, certains clients verront leur temps de chargement augmenter.

 Les fichiers statiques doivent être distribués par le serveur de distribution

Il est tout à fait possible de distribuer des fichiers statiques (images, vidéos, documents etc..) par votre serveur d'application, mais cela n'est généralement pas une bonne idée. En effet, la plupart des serveurs applicatifs sont écrit dans un langage interprété (Python, Ruby, JS, PHP etc..), qui est généralement 100x plus lent qu'un langage compilé comme C ou C++.

La plupart des serveurs de distribution comme Nginx ou Apache HTTP sont écrits dans un langage compilé, les rendant alors parfait pour se charger de la distribution très rapide de fichiers statiques.

Assurez-vous que les fichiers statiques sont bien distribués par votre serveur de distribution à l'aide d'une configuration appropriée.

Pour les applications web d'envergure internationale, vous pouvez aussi tout à fait donner la tâche de distribution à des services de CDN (content delivery network), qui agissent comme miroirs de vos fichiers statiques (généralement volumineux) en en faisant des copies sur des serveurs aux 4 coins de la planète, réduisant alors la distance de transport des fichiers entre le client et le serveur, contribuant aussi pour dé-saturer le réseau internet.

Plus de 15 entreprises font confiance à DASIO pour leur systèmes d'information

Rejoignez-nous et passez à la vitesse supérieure.

 Les caches doivent être toutes activées

Les systèmes de caches ont étés inventés expressément pour réduire  la charge de travail des serveurs. Toute fonction avec des entrées X dont la sortie est toujours la même peut être mis en cache.

Tout ce qui peut être pré-compilé (par exemple un code Python pré-compilé en .pyc,  un template Twig converti en PHP, des images générées en plusieurs dimensions), peuvent être mis en cache, réduisant alors considérablement la charge du serveur en évitant faire du travail en double.

Dans l'idéal mais pas toujours possible, vous pouvez mettre en cache directement les pages web par le serveur de distribution, les transformant alors en fichiers statiques, vous économisez alors en terme de temps d’exécution du code, et des appels à la base de données. Toutefois, avec un site web dynamique comme un commerce électronique par exemple, vous devrez configurer votre application à envoyer un cookie indicatif au client lorsque ce dernier s'est authentifié ou a ajouté quelque-chose à son panier; pour que le serveur de distribution sois avisé de générer une nouvelle page web plutôt qu'envoyer la page web compilée en cache (fichier statique).

Il est même possible de mettre ces fichiers de cache dans le répertoire /dev/shm de votre serveur. Ce répertoire est en réalité un espace de la mémoire vivre directement utilisable en tant que système de fichier. La mémoire vive est généralement 6x plus rapide d'accès que le disque-dur. En utilisant ce truc, assurez-vous toutefois de configurer les permissions du répertoire parent pour que uniquement le user de l'application sois en mesure de lire ce qui est à l'intérieur.

Vous devez évidemment vous assurer que toutes les caches fournies par votre application sont activées.

 Sessions stockées en mémoire plutôt que dans la base de données

Une véritable application web va en environ faire 10 requêtes pour être en mesure de générer une page web. Une de ces requêtes que vous pouvez éliminer est tout simplement le token de session car pouvoir réduire le 1/10 des requêtes à la DB n'est pas négligeable. Par exemple un serveur HTTP qui doit rouler 1000 requêtes par seconde, fera environ alors 10 000 requêtes par seconde à la DB, vous économisez alors environ 1000 requêtes à la DB par seconde.

Une infrastructure nécessitant plusieurs serveurs applicatifs sur différentes machines doit utiliser un serveur du type Memcache pour s'assurer le partage des sessions entre les différents nœuds du cluster. Autrement vous pouvez tout simplement utiliser le /dev/shm et y déposer les fichiers de session.

 Une base de données enterprise-ready est indispensable

Vous avez utilisé SQlite ou autre moteur dans le même genre pour déployer votre application en production ? Même si cela est pleinement fonctionnel et très facile à mettre en place, SQlite est principalement conçu pour les applications locales (ex: applications mobiles) et les environnements de développement.

Lorsqu'il s'agit d'un serveur qui doit traiter de nombreuses demandes de différents clients, il est primordial d'utiliser un véritable moteur de base de données enterprise-ready comme MySQL, MariaDB ou PostgreSQL. En effet, la différence de performance entre ces 2 types de serveurs peut avoir un écart extrêmement apparent; et cela peut empirer drastiquement selon le nombre de lignes et le nombre de curseurs. De plus, SQlite n'est pas conçu pour la concurrence, c'est à dire qu'en plus d'être naturellement lent, il ne peut traiter qu'une seule requête à la fois, faisant alors s'empiler une file d'attente importante.

 Limiter le nombre de requêtes par seconde

Les 2 facteurs habituels qui peuvent clairement ralentir votre serveur sont les robots des moteurs de recherche, et les jeunes futurs informaticiens qui viennent de découvrir les attaques de denial of service.

Par exemple, si sur votre serveur il y a 15 sites web avec chacune 2000 pages publiques, et qu'il y a 5 robots différents (ex: Google Bot, Bing Bot, Alexa Bot, Baidu Bot, Majestic12 Bot) en même temps qui indexe 1 page par seconde sur chacun des sites, il s'agit là de 75 requêtes par seconde en tout temps (étant donné des ~ 2000 pages).

Ce que vous pouvez faire c'est de configurer votre serveur de distribution (Apache, Nginx etc..) pour limiter le nombre de requêtes (429 Too Many Requests) à une aux 10 secondes par adresse IP sur les user agents qui contiennent le mot "Bot", passant alors à  environ 7.5 requêtes par secondes au total (considérant l'exemple précédent).

Cette dernière solution peut aussi vous protéger en quelque sorte contre les attaques de denial of service non distribuées, lorsque vous considérez tous les user agent. Par exemple, vous pouvez limiter le nombre de requêtes à 4 par secondes par adresse IP, vous protégeant alors contre la majorité des outils d'attaque, ou tout simplement l'attaque de tenir la touche F5 enfoncée (F5 = rafraîchir la page, tenir enfoncé = rafraîchit la page beaucoup de fois par seconde), surchargeant alors le serveur d'application qui doit traiter toute ces demandes malicieuses.

 Des locks sur tous vos cron jobs

Il n'est généralement pas très sûr d'utiliser un cron job sans lock. Lorsqu'un cron job est exécuter, celui-ci doit s'assurer qu'il n'est pas encore en opération.

Par exemple si vous avez un cron à toute les 30 minutes pour un truc qui tiens à jour un index de recherche, si l’exécution précédente n'est pas encore terminée car plus longue que prévue (pour différentes raisons légitimes), la nouvelle instance va s’exécuter quand même, pouvant causer par exemple la base de données à entre-locker les différentes instances, faisant qu'elles ne peuvent jamais se terminer; de nouvelles instances continuent à s’exécuter à tous les 30 minutes, ne se terminant jamais ainsi de suite .. Causant des problèmes de stabilité et de lenteur au début, jusqu'à ce que toute la mémoire / CPU du serveur sois utilisé par ces processus défaillants.

Lorsqu'un processus est créé à partir d'un cron job, un exemple lock de  simple fonctionne comme suit :

  1. Vérifier si le fichier vide /run/lock/supercoolapp-update-index existe

  2. S'il existe, quitter le processus, sinon continuer

  3. Créer le fichier vide /run/lock/supercoolapp-update-index

  4. Exécuter le processus jusqu'à la fin

  5. Supprimer le fichier /run/lock/supercoolapp-update-index

De cette manière vous pourrez gagner en vitesse et en stabilité globale.


Conclusion

Nous espérons que cet article aura pu vous aider à mieux comprendre les différentes techniques de base pour l'amélioration des performances du serveur d'une application web.

Dans le futur, nous rédigerons un article sur les trucs de performances orienté d'avantage du côté client que serveur.

Sentez-vous libre de nous contacter pour plus de détails.

Voir nos services en infrastructure >>