Gatling : l'art de détruire vos applications Web pour les rendre plus résilientes

D2SI_Blog_Image_Gatling.png

Attention : utiliser Gatling sur votre application en production peut être dangereux, ne pas laisser entre toutes les mains.

Nous allons passer ensemble en revue tous les concepts fondamentaux de Gatling et démontrer un cas d'utilisation simple. Si vous avez déjà vécu des problèmes de performance sur votre application ou des mises en prod catastrophiques, cet article est pour vous.

Un test de performance, c’est quoi ?

Un test de performance permet de vérifier la performance et la robustesse d’une application. Le principe est de simuler un grand nombre d’utilisateurs concurrents se comportant de la façon la plus réaliste possible. Cela permet d’anticiper les crashs mais aussi d’avoir une vision la plus précise possible des temps de réponse que vivent tous vos utilisateurs, et notamment ceux qui ont les temps de réponse les plus longs.

Nous pouvons distinguer plusieurs types de tests de performance :

  • Test de charge : Simulation de la charge attendue d’utilisateurs
  • Test de capacité : Simulation où on augmente le nombre d’utilisateurs par paliers, afin de déterminer quelle est la limite du système
  • Test de stress : Simulation d’un grand nombre d’utilisateurs arrivant en même temps, par exemple dans le cas d’une pub à la télévision ou d'une campagne de marketing viral sur les réseaux sociaux
  • Test d’endurance : Simulation d’une charge importante sur une longue durée, afin de vérifier que l’application ne souffre pas de dégradation de performances

L'importance de tester au plus tôt

Il nous est souvent demandé de réaliser des tests de performance, dans le meilleur des cas quelques semaines avant la mise en production, dans le pire des cas après un premier incident en prod. Dans le premier cas, le temps est trop court pour rectifier le tir, dans le deuxième cas l’incident a déjà eu lieu. Ce n’est pas inutile, car cela permet tout de même de voir où se situe le problème, mais il existe une manière plus efficace de faire du test de performance : dès les débuts du cycle de développement. Oui, pas besoin d’attendre la fin ! Plus la détection des problèmes se fait tôt, plus les changements interviennent tôt dans le cycle de développement, économisant du temps et de l’argent, tout en améliorant la qualité de votre application. Les tests de performances sont également appréciés des directions techniques, car ils permettent de savoir à l’avance l’infrastructure nécessaire pour mettre une application en production.

Gatling, outil de test de performance

Gatling est un outil Open-Source de test de performance spécialisé dans les applications web. Il est développé par la société française Gatling Corp (passez-nous voir à Bagneux en région parisienne!), et fonctionne sur tous les systèmes d’exploitation.

Gatling va nous permettre d’écrire simplement des tests de performance et de les faire évoluer au cours de son développement. C’est un outil performant, qui permet de générer de nombreux utilisateurs avec peu de machines. Les rapports Gatling se présentent sous la forme de fichiers HTML, visible avec un navigateur internet.

Créer un test Gatling

Les tests Gatling sont décrits sous la forme de fichiers Scala. Cependant, une connaissance approfondie du langage Scala n’est pas nécessaire, Gatling utilisant un DSL (langage dédié) simple. Vous pouvez également vous servir du recorder Gatling pour créer vos tests. Celui-ci enregistrera toutes vos actions sur votre site web, et les retranscrira en un test Gatling prêt à l’emploi.

Lancer un test Gatling

Une fois le test Gatling écrit, vous pouvez le lancer directement depuis le bundle Gatling, ou depuis votre outil de build préféré (Maven, Sbt, Gradle). Pour vous montrer comment Gatling fonctionne, nous allons tester notre script Gatling sur ce site, qui est une application basique gérant un stock d’ordinateurs.

Configuration HTTP

La première étape est de préciser la configuration HTTP, qui va nous permettre notamment de définir l’URL de base de nos requêtes (utilisée quand on utilise une url relative), et les headers HTTP envoyés à chaque requête :

val httpConf = http
  .baseURL("http://computer-database.gatling.io") // Base des URL relatives
  .acceptHeader("text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8") // Headers envoyés à chaque requête

Création de requêtes

Nous allons maintenant créer notre scénario d’utilisation, nommé D2siSimulation :

val scn = scenario("D2siSimulation")

Plusieurs mots-clés sont à analyser :

Ajoutons maintenant au scénario l'exécution d'une requête simple.

val scn = scenario("D2siSimulation").exec(http("Get Computers").get("/computers"))
  • exec indique que l’on va exécuter une action
  • http indique qu’il s’agit d’une requête HTTP nommé Get Computers
  • get indique que la requête GET est sur le endpoint /computers, à partir de la baseURL définit plus haut

Cherchons maintenant à créer un nouvel ordinateur.

val scn = scenario("D2siSimulation")
  .exec(http("Get Computers").get("/computers"))
  .exec(http("Add Computer").post("/computers")
  .formParam("name", "Beautiful Computer")
  .formParam("introduced", "2012-05-30")
  .formParam("discontinued", "")
  .formParam("company", "37"))

Nous utilisons cette-fois ci la méthode post pour préciser que l’on veut une requête HTTP de type POST. Nous définissons ensuite les paramètres avec la méthode formParam (le paramètre name aura comme valeur Beautiful Computer, …).

Dans cet exemple Gatling enverra une requête GET puis une requête POST. Cependant, cela ne simule pas véritablement le comportement d’un utilisateur qui va analyser la page avant d’appuyer sur un bouton. Ajoutons une pause de 5 secondes entre les deux requêtes.

val scn = scenario("D2siSimulation")
  .exec(http("Get Computers").get("/computers"))
  .pause(5)
  .exec(http("Add Computer").post("/computers")
  .formParam("name", "Beautiful Computer")
  .formParam("introduced", "2012-05-30")
  .formParam("discontinued", "")
  .formParam("company""", "37"))

Mise en place

Nous allons maintenant relier notre configuration HTTP et nos requêtes, afin d’avoir un script Gatling fonctionnel. Précisons également notre profil d’injection: ajout de 100 utilisateurs en même temps.

setUp(scn.inject(atOnceUsers(100)).protocols(httpConf))

Le mot clé setUp nous permet de commencer la phase de définition. On va ensuite injecter 100 utilisateurs en même temps avec les mots clés inject et atOnceUsers. On précise ensuite que l’on va utiliser notre configuration HTTP avec le mot clé protocols.

Il est également possible de faire des profils d’injections plus compliqués: arrivée constante d’utilisateurs, arrivées par palier, arrivées à des moments précis…

Assertions = critères d’acceptance

Gatling est maintenant capable de lancer notre scénario avec 100 utilisateurs. Mais si l’on veut vérifier, par exemple, que chaque requête ait un temps de réponse inférieur à deux secondes, on peut demander à Gatling de vérifier lui-même cette condition. On appelle cette vérification une assertion.

setUp(scn.inject(atOnceUsers(100)).protocols(httpConf))
  .assertions(global.responseTime.max.lt(2000))

L’ajout d’assertions se fait avec le mot-clé assertions, à la suite du setUp. On définit ensuite que, globalement, le temps de réponse maximal doit être inférieur à 2000 millisecondes, soit 2 secondes.

Assemblage du script

La dernière étape est d’assembler tous les morceaux de scripts précédemment écrits. Pour que Gatling reconnaisse notre scénario, nous englobons notre script dans une classe, qui étend la classe Simulation.

class D2siSimulation extends Simulation {
  val httpConf = http
    .baseURL("http://computer-database.gatling.io") // Base des URL relatives
    .acceptHeader("text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8") // Headers envoyés à chaque requête

  val scn = scenario("D2siSimulation")
    .exec(http("Get Computers").get("/computers"))
    .pause(5)
    .exec(http("Add Computer").post("/computers")
    .formParam("name", "Beautiful Computer")
    .formParam("introduced", "2012-05-30")
    .formParam("discontinued", "")
    .formParam("company", "37"))

  setUp(scn.inject(atOnceUsers(100)).protocols(httpConf))
    .assertions(global.responseTime.max.lt(2000))
}

Présentation des résultats

Pour que les résultats du test soient plus intéressants à regarder, j'ai modifié légèrement la configuration du profil d'injection, mais la configuration des requêtes reste la même.

Les résultats de notre test sont visibles en temps réel sur la console qui a lancé le test. Cet affichage sommaire présente l'avancement des requêtes et des utilisateurs, avec un résumé à la fin.

Le rapport HTML nous donne de nombreuses informations supplémentaires, notamment les temps de réponses de chaque requête.

Ou même l'évolution du nombre d'utilisateurs au cours de la simulation.

Ainsi que plus d'informations sur les temps de réponse et leur distribution.

Sans oublier le nombre de requêtes et réponses.

Résultats avec FrontLine

Gatling Corp développe aussi FrontLine, une version entreprise de Gatling. FrontLine est une application web permettant de gérer facilement ses tests Gatling, et de les lancer de manière distribués (pour des tests à très grande échelle et/ou géographiquement distribués), avec plus de données remontées (connexions TCP, bande passante, résolutions DNS, monitoring des injecteurs de charge).

Vous pouvez voir les résultats de notre simulation Gatling ci-dessous, avec notamment l'évolution du nombre de requêtes et réponses, ainsi que leur temps de réponse.

Conclusion

Nous avons donc appris ce qu'était un test de performance, et comment en créer un simplement avec Gatling. Vous pouvez maintenant vérifier que votre prochaine application tiendra bien la charge. Plus besoin de croiser les doigts à votre prochaine mise en prod !

Si vous voulez tester par vous-même, rendez-vous sur le site de Gatling. Et pour plus d'informations sur les autres fonctionnalités de Gatling, consultez notre documentation.

Retrouvez la simulation réalisée lors de cet article sur Github

Et bien sûr, n'hésitez pas à nous contacter : contact@gatling.io