Initiation à Gatling, un outil de tests de charges

0

Gatling-logo

Après avoir développé, testé fonctionnellement une application, une autre étape importante consiste à tester l’application dans des conditions plus proches de la production. Pour cela, une étape de tests de charge semble indispensable. En effet, une application est souvent testée unitairement par un développeur, profil après profil, fonctionnalité après fonctionnalité.

Nous allons nous intéresser ici à un outil permettant de réaliser ces tests de charge, à savoir Gatling. Gatling est un outil basé sur le langage Scala, Netty et Akka. Grâce à Netty, les requêtes sont lancées de manière asynchrone, ce qui permet d’utiliser un grand nombre d’utilisateurs virtuels. Les scénarios générés sont écrits en langage Scala. Ce langage est proche de Java mais est plus simple à appréhender. Quant à Akka, c’est un Framework qui implémente le modèle d’acteurs.

Gatling s’installe facilement grâce à un fichier zip. Étant basé sur Akka, il a néanmoins besoin de la présence du JDK pour fonctionner. Il suffit ensuite de paramétrer manuellement son navigateur pour qu’il passe par le port 8000.

Création du scénario

Les scénarios peuvent être écrits entièrement à la main, ou nous pouvons utiliser le « recorder ». Celui-ci permet d’enregistrer un scénario simplement en navigant dans l’application, à la façon d’un internaute. Le scénario généré peut ensuite être modifié à l’envi à l’aide du langage Scala.

Nous allons ici tester Gatling dans sa version la plus simple. Prenons par exemple une application de réservation de billets de train :
– Ouvrir l’URL du site
– Rechercher un billet aller-retour Paris-Marseille pour une personne avec une carte jeune de réduction
– Accéder à l’ensemble des tickets disponibles
– Choisir le trajet aller et valider
– Choisir le trajet retour et valider
– Renseigner les données pour le mail récapitulatif
– Accéder à la page de paiement

L’entête du fichier est généré automatiquement, suivant les informations renseignées dans la fenêtre de Gatling. Par défaut, le nom de la classe est « RecordedSimulation ». Le nom du package doit commencer par une lettre est doit être en minuscule.

Le fichier résultant va être créé dans le répertoire paramétré dans « Output folder » et, dans notre exemple, va porter le nom « RecordedSimulation.scala ».

 

1

 

Dans l’entête du fichier généré se retrouve le nom du package et du nom de la classe. De plus, la variable baseURL indique l’adresse principale du site testé. Les fichiers générés peuvent être modifiés grâce à un éditeur de texte type Notepad++. Ils peuvent également être ouverts dans Eclipse, si celui-ci est présent sur le poste.

 

2

 

Lorsque l’on navigue sur une page web ou que l’on saisit des informations dans un formulaire, toutes les URLs sont enregistrées par Gatling. Un extrait des actions exécutées lors de la saisie des villes de départ et de destination est présenté, ici un Paris-Marseille aller-retour. Plusieurs fonctions similaires sont générées car la fonction d’autocomplétion est enregistrée.

 

3

 

Ce type d’enregistrement génère un grand nombre de requêtes. Nous pouvons modifier leur nom, afin de les rendre plus parlantes. Cela permettra de les retrouver plus facilement dans les graphiques de résultats. Nous allons détailler la saisie des informations après validation des billets aller et retour et avant la page de paiement. Le site demande la date de naissance du voyageur, ses nom et prénom ainsi que son adresse email.

Le nom de la requête a été ainsi modifié :

 

4

 

Ce morceau de scénario peut être écrit comme une fonction. Ceci permet de factoriser le code et de potentiellement le réutiliser par la suite.

 

5

 

La génération des scénarios est donc facilitée par l’utilisation des fonctions. Un scénario se compose d’une succession de fonctions. Une même fonction pouvant être appelée par plusieurs scénarios, il est donc possible de lancer plusieurs fois les mêmes actions, par exemple pour des profils différents.

 

6

 

Pour l’instant, le scénario est exécuté par une seule personne avec des informations de date de naissance, de nom, de prénom et d’adresse email fixes. Nous pouvons pousser le paramétrage de la fonction de saisie d’information avec un fichier. Dans notre exemple, nous pouvons mettre en facteur le jour, le mois et l’année de naissance, les nom et prénom ainsi que l’adresse email.
Le fichier nécessaire à ce paramétrage doit être un fichier au format CSV. Le nom des colonnes doit reprendre le nom des variables qui sera utilisé par la suite dans le scénario. Un exemple de fichier est présenté ci-dessous. Notre fichier se nomme param.csv.

 

7

 

Le nom du fichier paramétré doit être inséré dans la fonction où il est appelé, avant l’appel des variables. Les variables ainsi définies sont appelées grâce à l’écriture ${nomvariable}, ce qui donne un fichier plus générique.

 

8

 

Paramétrage du test de charge

Pour l’instant, un seul utilisateur se connecte à l’application. Pour faire des tests de charge probants et simuler les vraies conditions de navigation, le nombre d’utilisateurs peut être augmenté. Par exemple, 6 utilisateurs peuvent se connecter à l’application toutes les 15 secondes grâce aux fonctions inject et rampUsers.

9

Si nous voulons plutôt tester des utilisateurs simultanés, il faut utiliser la fonction atOnceUsers avec le nombre d’utilisateurs en paramètre. Par défaut, le scénario est enregistré avec un seul utilisateur.

10

 

Affichage des résultats

Les résultats sont ensuite disponibles, après leur génération dans un répertoire /results. Un nouveau dossier est généré, portant le nom de package, suivi d’un tiret et de plusieurs chiffres. Les résultats sont fournis au format HTML et la page racine, index.html, permet d’accéder à l’ensemble de ces derniers.

Un premier graphique en bâtons fournit une vue du temps de réponse des requêtes. Les requêtes sont partagées en différentes catégories suivant leur temps de réponse. Dans notre cas avec un utilisateur, nous pouvons voir que plus de 400 requêtes répondent en moins de 800 ms, mais qu’un certain nombre sont en erreur, dans la colonne ‘failed’.

 

11

 

Dans ce deuxième graphe en bâtons, où 10 utilisateurs sont envoyés en simultané, il est possible de voir que le résultat est plus nuancé. On peut noter que l’écrasante majorité des requêtes répondent en moins de 800 ms à quelques exceptions près. On retrouve également des requêtes en erreur.

 

12

 

La page index.html contient également un tableau récapitulatif de toutes les requêtes. Pour chaque ligne, on peut voir le nombre de requêtes total, le nombre de requêtes OK, le nombre de requêtes KO ainsi que les temps de réponses.

 

13

 

En cliquant sur le nom d’une requête, une nouvelle page s’ouvre, pour afficher les résultats de la requête spécifiquement. Par exemple, si on ouvre la request_41, il est possible de voir que 7 requêtes ont abouti en moins de 800 ms et 3 n’ont pas abouti.

 

14

 

D’autres types de graphiques sont également disponibles. Par exemple, nous pouvons voir le nombre de requêtes par utilisateur ou le nombre de réponses par utilisateur.

Ces analyses permettent de voir quelles requêtes sont les plus problématiques, et de corriger le tir rapidement.

 

Synthèse

Gatling dispose de nombreux points forts :

  • Cet outil est gratuit et simple à installer. Il ne requiert aucun logiciel supplémentaire hormis un JDK à jour.
  • La facilité d’utilisation du recorder : avec un simple navigateur et un minimum de paramétrage, il est possible d’instancier de nombreux scénarios, des plus simples au plus complexes.
  • L’existence d’un plug-in pour Eclipse : pour les développeurs Java, il devient simple de lancer le recorder, ou par la suite de lancer les tests de charge.
  • La possibilité de créer des scénarios ou de les modifier grâce au langage Scala. Le langage Scala est suffisamment simple et « parlant » pour pouvoir être utilisé facilement.
  • La clarté du site : La documentation sur le site est assez complète pour faire des scénarios simples, puis pour les complexifier par la suite.
  • La détection des problèmes de compilation : lorsque nous essayons de lancer les tests de charges, l’outil pointe les erreurs de compilation dans la console.
  • La clarté des graphiques générés : nous pouvons faire aisément des analyses globales ou des analyses plus détaillées, requête par requête.

 

Gatling a aussi ses points faibles :

  • La génération différée des rapports : les rapports sont générés après l’exécution de la totalité des tests de charge.
  • L’interface qui permet de lancer les tests de charge, dans sa version native, n’est pas graphique.

 

En conclusion, Gatling gagne à être connu et utilisé dans le cycle de vie d’un projet. Avec un minimum de connaissance en langage informatique, des scénarios complexes peuvent être élaborés. Cela permet de pointer du doigt les portions d’application à modifier ou à optimiser. Je l’ai testé dans sa version la plus basique. Malgré un manque d’ergonomie dans l’outil de tests de charges en lui-même, les fichiers générés se retrouvent facilement et le paramétrage est aisé.

 

CQRS, petites recettes pour vos architectures web

0

Architecture, CQRS, de quoi parle-t-on ?

CQRS, définition

CQRS est un acronyme pour Command Query Responsability Segregation. Il s’agit d’un pattern d’architecture qui vise à séparer les couches de lecture et d’écriture.

Le besoin est né du constat que la plupart des architectures proposent une unique couche d’accès aux données permettant de faire la lecture et l’écriture. Or, dans la plupart des applications, les besoins en écriture ne coïncident pas avec ceux en lecture.

Prenons l’exemple d’un quelconque site de e-commerce, là où les infos de la fiche produit seront lues des milliers de fois chaque jour (parfois chaque seconde), la mise au panier n’interviendra qu’à un rythme beaucoup moins soutenu.

D’une manière générale, on va séparer (Segregation) la responsabilité (Responsability) des lectures (Query) et écriture (Command).

Néanmoins, la mise en place de CQRS n’est pas chose aisée et peut poser quelques problèmes.

CQRS, problématique

Quand on essaie pour la première fois de comprendre CQRS, on se retrouve devant quelque chose de passablement abstrait et plutôt difficile à intégrer avec de réels problèmes :

  • Comment je vais pouvoir implémenter ça ?
  • Comment adapter ces concepts à mon contexte actuel ?
  • Est-ce pertinent pour mon besoin ?
  • Est-ce que ça ne va pas coûter trop de temps par rapport au gain final ?

Toutes ces questions sont centrales et pertinentes. On ne doit pas se lancer dans un modèle d’architecture sans en maîtriser les tenants et aboutissants, sans être capable d’en tirer ce qui est pertinent pour notre besoin du moment et sans s’assurer que notre projet aura à y gagner dès le départ.

 

Architecture, niveau 1, le monolithe

On avait une petite application à coder, on est pros, on est propres, on a fait du n-tiers qui marche bien :
1
C’est beau, ça marche, tout le monde est content…

Et puis il faut ajouter de nouvelles fonctionnalités, modifier et enrichir les existantes…

On se rend alors compte qu’on a codé un véritable monolithe avec un couplage fort entre les couches et que chaque évolution impacte toutes les couches. Et comme disait nos ancêtres les gaulois, « bouger un monolithe, c’est dur », avant l’arrivée de la potion magique…

Pour pallier à tout ça, en tant que bons concepteurs orientés objet que nous sommes, on va découpler un peu tout ça pour rendre ce monolithe un peu plus souple.

 

Architecture, niveau 2, le découplage

On ne va plus appeler directement chacune des couches, mais on va passer par des interfaces, ça nous permettra de mettre un peu de généricité et de mutualiser du code. Ça nous permettra de mettre en place des tests unitaires pour se protéger lors des prochaines évolutions !

On va également isoler chaque élément de domaine afin de cloisonner les fonctionnalités pour prévenir les régressions intempestives !
Petit exemple avec le domaine « Command » qui va permettre de gérer toutes les commandes de notre superbe application de e-commerce !

2

Ah, là, on est bien… Enfin… Si on met ça en place pour chaque classe de domaine qu’on possède….

3

On n’aurait pas cassé le RAP (Reused Abstraction Principle) ? On a mis en place un niveau d’abstraction (notre interface) qui n’est utilisé qu’une seule fois ! Le principe de l’abstraction est de pouvoir être réutilisé ! Sans ça, on ne profite pas de ce que l’abstraction aurait à offrir et on ajoute de la lourdeur aux instanciations et aux tests unitaires !

Et quand viennent les nouvelles demandes d’évolution, on se retrouve face à un nouveau monolithe… Découplé, mais monolithe quand même ! Et on nous demande de nouvelles vues, de nouvelles données, sauf qu’à chaque modification de table, on casse toutes nos implémentations, modèles et vues actuels… Accessoirement, l’application commence à poser des problèmes de performances…

Il va peut-être falloir commencer à séparer les modules de lecture et d’écriture…

 

Architecture, niveau 3, CQRS (un peu)

Quels sont les éléments les plus affichés dans mon application ? La fiche article ? Le panier ? Les promos ? Très bien, on va créer une autoroute pour ces éléments-là. Pour le reste, on va s’assurer que nos données restent fiables.4

On possède une base en lecture dénormalisée, nos performances sont maintenant bien meilleures ! On a mis en place un module qui répercute les écritures sur la base de lecture et tout va bien non ? Pas tout à fait…

La mise en place de ce mode de codage reste coûteuse et le faire pour chaque domaine fonctionnel de l’application s’annonce beaucoup trop long. Les demandes d’évolutions vont s’empiler et le client va s’impatienter… Il faut donc prioriser les domaines à basculer, mais comment choisir ?

Pour répondre à cette interrogation, il convient de répondre à une question fondamentale : quel est l’avantage stratégique du métier ? Sur quoi veut-il faire la différence ? Quel est son plan de développement business à moyen terme ?

On va ainsi devoir dialoguer avec le métier, et pour le faire efficacement, il conviendra de modéliser ses concepts en termes de domaines, d’événements faits… Tous les acteurs du projet, fonctionnels ET techniques y trouveront du sens !

 

Architecture, niveau 4, CQRS (un peu plus avec du DDD)

Maintenant que « tout le monde » fait du scrum, on va s’appuyer sur notre pratique des user stories pour orienter tout ça vers les notions de faits et d’événements. Mon client peut ajouter un produit à son panier ? Il en découle un fait, une intention, ‘AjoutProduitAuPanier’ et un événement (immuable puisque déjà passé) ‘ProduitAjoutéAuPanier’. Au niveau code, on va transformer ces intentions en Command et ces événements en Events. Regrouper ces faits et événements par éléments de domaine (panier, commande, navigation…) est hérité du DDD (Domain Driven Design ou Conception Dirigée par le Domaine). On dispose ici d’un langage commun avec le métier et d’un parallèle fort entre l’organisation métier et technique ce qui va faciliter la lecture du code pour les futurs entrant.

Côté technique, les actions utilisateurs vont déclencher des commandes (réversibles) suivies par des événements (immuables). Il s’agit ensuite de bien organiser tout ça afin d’assurer la pertinence du tout.

5

Tout ce petit monde fonctionne à merveille, mais maintenant que le business tourne bien, le marketing, la direction et d’autres entités du métier se sont greffés au projet et aimeraient obtenir des informations sur les comportements utilisateurs (ajout/suppression du panier, articles explorés avant ajout au panier…). Comment faire ça sans ré-impacter tous les scénario de l’application ? Il nous faudrait un genre de machine à voyager dans le temps !!!

 

Architecture, niveau 5, CQRS (encore plus, avec DDD et ES)

On a une machine à voyager dans le temps ! Ce sont nos événements ! Pour permettre le voyage dans le temps, il convient de stocker tous ces événements. On pourra ainsi reconstruire tout un cheminement à travers l’application en temps réel. On pourra également restaurer avec finesse à un état antérieur : la sauvegarde du panier a planté ? Peu importe, on sait quels articles ont été ajoutés puis enlevés du panier ! On appelle ce concept l’Event Sourcing (ES).

6

C’est ce genre de fonctionnement qui est utilisé par les banques pour calculer avec certitude un solde sur un compte bancaire. Le solde n’est pas enregistré tel quel, il est recalculé à partir des mouvements successifs et peut donc être retrouvé à n’importe quel jour du mois avec beaucoup de facilité.

 

Architecture, CQRS, pourquoi, où, comment, combien ?

CQRS peut nous aider à résoudre des problématiques particulièrement complexes. Néanmoins, sa difficulté de mise en œuvre doit inciter à la prudence. Il faut se poser les bonnes questions :

  • Ai-je réellement besoin d’un modèle aussi complexe ?
  • Ai-je besoin de tous les niveaux d’implémentation ?
  • Ai-je l’équipe me permettant de mettre en place ces concepts ?
  • Est-ce que je suis bien outillé pour le faire ?

Face à une problématique métier forte, ce qui importe plus, c’est la capacité de l’architecte à faire preuve de pragmatisme et à préférer une solution adaptée à une solution élégante.  Il faut également se souvenir que c’est le besoin métier qui doit diriger nos choix en architecture et non l’application qui doit piloter les besoins métiers.

 

CQRS, quelques ressources

Cet article a été très largement inspiré (jusqu’au titre) de la présentation faites par Thomas Jaskula (http://blogs.developpeur.org/tja/) au MUG Lyon le 24/06/2016 : http://fr.slideshare.net/tjaskula/cqr-sv2

J’ai également utilisé quelques sites en amont pour dégrossir le sujet : http://blog.eleven-labs.com/fr/cqrs-pattern/ , http://martinfowler.com/bliki/CQRS.html

Quelques notions sur le DDD peuvent s’avérer fort utiles : http://blog.xebia.fr/2009/01/28/ddd-la-conception-qui-lie-le-fonctionnel-et-le-code/ , http://blog.octo.com/domain-driven-design-des-armes-pour-affronter-la-complexite/

Enfin des bouquins de référence proposé par Thomas Jaskula lors de la présentation : https://www.amazon.fr/Implementing-Domain-Driven-Design-Vaughn-Vernon/dp/0321834577 et https://www.amazon.fr/Patterns-Principles-Practices-Domain-Driven-Design/dp/1118714709