Cet article fait suite à une présentation faite aux Human Talks en septembre 2022. Les slides sont disponibles ici.
Pendant ma mission chez RCA en 2021/2022, j'ai intĂ©grĂ© une Ă©quipe travaillant sur des API publiques mises Ă disposition derriĂšre un API Manager, Gravitee. Cette plateforme permet, entre autres, de contrĂŽler et de sĂ©curiser nos APIs. Par exemple, le ârate limitâ consiste Ă limiter le nombre dâappels Ă une API dans un intervalle de temps donnĂ©.
MalgrĂ© la mise en place de cette protection, il Ă©tait important, pour nous, de pouvoir estimer la limite dâutilisation de notre API et de notre plateforme. Avec lâarrivĂ©e de nouveaux clients, lâutilisation de lâAPI, et donc les montĂ©es en charge, vont ĂȘtre progressives.
Le terme âtir de chargeâ peut ĂȘtre dĂ©fini de maniĂšres diffĂ©rentes en fonction des profils des personnes ou des entreprises.
Pour nous, lâexĂ©cution de tirs de charge consiste Ă simuler un nombre important dâappels Ă notre API sur un temps trĂšs court, lâobjectif Ă©tant dâarriver Ă âfaire exploserâ notre API.
Gatling : notre premier test de charge
Pour trouver ces limites, nous nous sommes orientĂ©s vers la solution Gatling. Pourquoi ne pas utiliser dâautres outils plus ârapidesâ comme wrk, un outil en ligne de commande nâimpactant pas les dĂ©veloppements ?
Chez RCA, lâĂ©co-systĂšme des tests est construit avec lâAPI KaratĂ©, un outil de test automatisĂ© qui se veut simple dâutilisation, que lâon soit dĂ©veloppeur·se ou personne moins technique.
Avec le langage Gherkin, nous allons pouvoir rédiger nos scénarios de tests. Par exemple :
Feature: Guess the word
# The first example has two steps
Scenario: Maker starts a game
When the Maker starts a game
Then the Maker waits for a Breaker to join
Gatling peut réutiliser ces scénarios Karaté pour exécuter des tirs de charge. Son intégration dans une API est trÚs simple. Il suffit de récupérer la dépendance karate-gatling :
<dependency>
<groupId>com.intuit.karate</groupId>
<artifactId>karate-gatling</artifactId>
<scope>test</scope>
</dependency>
Lorsque nous avons initié ces travaux autour des tests de charge, seul le DSL Scala était disponible. Un DSL Java a depuis été créé.
class GatlingSimulation extends Simulation{
val test = scenario("tirs-charge").exec(karateFeature("classpath:scenarios/charge/tirs-charge.feature"))
AprĂšs avoir crĂ©Ă© un profile Maven dĂ©diĂ© Ă ces tests de charge, la commande suivante permet dâexĂ©cuter notre classe GatlingSimulation : mvn clean test-compile gatling:test
Gatling gĂ©nĂšre par la suite un rapport HTML contenant des informations complĂštes sur le nombre de requĂȘtes envoyĂ©es, les temps de rĂ©ponse moyens, les statuts des requĂȘtes, telles que le montrent les rapports suivants :
Rapidement, nous pouvons constater sur le premier graphique que les indicateurs semblent Ă premiĂšre vue corrects. LâAPI a rĂ©pondu aux 3 000 requĂȘtes effectuĂ©es en 800 millisecondes maximum. Pour une API et au vu de notre expĂ©rience, câest une valeur tout Ă fait raisonnable.
Cette tendance est confirmée dans le diagramme en barres. Notre API a répondu sous les 200 ms.
Les fourchettes de temps de réponse, visibles sur les graphiques (800ms, 1200ms) sont des valeurs de base définies par Gatling.
LâintĂ©gration | automatisation dans CI/CD
Une fois le scĂ©nario Gatling Ă©crit, le tester en lâexĂ©cutant sur son propre poste est tout Ă fait possible. Un tir de charge sur une API dĂ©ployĂ©e sur son propre ordinateur lâest aussi. Mais Ă quoi bon faire ce test en local ou bien Ă partir de son poste ? Chaque tir dĂ©pendra de la puissance et de la capacitĂ© de votre ordinateur Ă cet instant. Si notre objectif est de trouver des limites Ă notre API, cela nâaboutira pas Ă un bilan pertinent.
Pour avoir des rĂ©sultats interprĂ©tables, nous avons dĂ©ployĂ© notre API sur une infrastructure âtestingâ, quasiment Ă©quivalente Ă celle de production. Elle est lĂ©gĂšrement sous-dimensionnĂ©e.
Comme dit prĂ©cĂ©demment, notre objectif Ă©tait de nous donner une limite de sollicitation pour que notre API ait des temps de rĂ©ponse acceptables. Il fallait donc que lâinfrastructure exĂ©cutant les scĂ©narios KaratĂ© soit assez puissante, ce qui nâest pas le cas de nos ordinateurs.
A quoi bon devoir installer des outils comme Maven pour pouvoir exécuter un tir de charge ?
Lâinterface utilisateur de GitLab, assez simple dâutilisation, permet Ă tout profil de dĂ©clencher rapidement des pipelines. Chez RCA, le mĂ©canisme exĂ©cutant les pipelines, les GitLab Runner, est installĂ© sur une infrastructure puissante.
Nous avons crĂ©Ă© un nouveau job âđ§Żtirs de charge sur testing đ„â dans notre pipeline GitLab existant pour rendre possible lâexĂ©cution un tir de charge sur notre API dĂ©ployĂ©e sur lâenvironnement iso production.
Les problÚmes rencontrés (et solutions)
Infrastructure as code
Lâappel Ă notre API est rĂ©alisĂ© aprĂšs avoir rĂ©cupĂ©rĂ© un jeton dâauthentification via Gravitee. Nous avions dĂ©fini un ârate limitâ Ă 1000 appels pour une pĂ©riode de 10 minutes. Lors de notre tir de charge nous Ă©tions sur plusieurs dizaines de milliers d'appels en 1 minute. Le ârate limitâ est logiquement atteint !
MalgrĂ© une modification de lâinfrastructure de test pour dĂ©sactiver ce ârate limitâ, un autre point de blocage est intervenu.
Call Single
Dans notre scĂ©nario KaratĂ©, 1 appel Ă lâAPI provoque 1 appel Ă Gravitee. Pour plusieurs dizaines de milliers dâappels, nous avons donc le mĂȘme volume de jetons crĂ©Ă©s. Cependant notre tir de charge nâa pas pour objectif de tester la robustesse de Gravitee. Dâautant plus que lâĂ©quipe ayant mis en place cette infrastructure sâĂ©tait assurĂ©e de sa performance.
Et dâun autre point de vue, gĂ©nĂ©rer autant de jetons nâa pas de sens, autant en crĂ©er un seul pour nos tests.
Pour faire cela, lâAPI KaratĂ© met Ă disposition une mĂ©thode callSingle pour nâappeler quâune seule fois une ressource, dans notre cas la gĂ©nĂ©ration du jeton.
var result = karate.callSingle('classpath:scenarios/api/authentification.feature', config);
Le rĂ©sultat de lâexĂ©cution de cette commande peut se vĂ©rifier dans le tableau gĂ©nĂ©rĂ© suivant, au niveau de la ligne entourĂ©e en rouge : 1 seule exĂ©cution sur la ressource POST /bearer/token a Ă©tĂ© produite.
Cela a pour consĂ©quence de devoir exĂ©cuter notre tir de charge pendant la validitĂ© du jeton, sous peine de gĂ©nĂ©rer un nombre important dâerreurs 401 - Unauthorized.
Analyse des résultats
Pour dĂ©terminer une limite Ă notre API, nous avons rĂ©alisĂ© plusieurs sĂ©ries de tirs de charge. DĂ©marrant Ă quelques centaines dâappels en une minute, nous avons progressivement augmentĂ© le nombre dâappels, rĂ©duit le temps dâexĂ©cution, jusquâĂ avoir des temps de rĂ©ponse dĂ©gradĂ©s pour une mise Ă disposition auprĂšs de nos clients.
Dans les graphiques suivants gĂ©nĂ©rĂ©s par Gatling, deux informations sont disponibles : le nombre de requĂȘtes et le nombre dâutilisateurs Ă un instant t.
Ces donnĂ©es sont stables et montrent quâaucun point de blocage nâest rencontrĂ© pendant le tir. Le nombre de requĂȘtes est stable.
En plus des schĂ©mas, Gatling offre un rapport textuel avec le nombre de requĂȘtes pendant le tir, le temps de rĂ©ponse maximum ainsi que des statistiques sur la rĂ©partition des temps de rĂ©ponse.
Une donnée importante et représentative de la santé de notre API est la consommation de CPU. Cette information est disponible dans les tableaux de bord Grafana existant au sein de RCA qui vont nous permettre de valider notre interprétation des résultats issus de Gatling.
Nous sommes arrivĂ©s Ă identifier une limite pour notre API. Un tir simulant 50 000 utilisateurs sur 30 secondes nous donne des temps de rĂ©ponse acceptables, câest-Ă -dire sous la barre des 800 ms.
La plateforme collaborative crĂ©Ă©e par RCA a une volumĂ©trie en production de 12 000 Ă 16 000 utilisateurs par heure. Sachant quâen plus, lâenvironnement est sous dimensionnĂ© par rapport Ă la production, ces 50 000 utilisateurs en 30 secondes pourront largement solliciter notre API.
Les apports de cette expérimentation
LâintĂ©gration des tirs de charge dans notre Ă©quipe Ă©tait une dĂ©couverte. AprĂšs avoir pris en main lâAPI KaratĂ© et lâextension Gatling, nous avons rapidement pu exploiter nos premiers rĂ©sultats et nous donner un ordre de grandeur de la capacitĂ© dâappels de notre API.
Heureusement que ces tests ont Ă©tĂ© rĂ©alisĂ©s, ils nous ont permis de dĂ©tecter et corriger un problĂšme de performance. Cela est rassurant pour la suite des Ă©volutions et nous protĂ©gera dâautres problĂšmes du mĂȘme genre.
LâintĂ©gration des tests de charge dans toute lâĂ©quipe est une bonne chose. Avec la sensibilisation et lâautomatisation des tirs de charge dans GitLab CI, tout profil de personne peut exĂ©cuter des tirs de charge, rĂ©cupĂ©rer le graphique et en faire une premiĂšre interprĂ©tation.
Sylvain Naël (RCA) / Jean-Philippe Baconnais (Zenika)
Retrouvez cet article sur le compte Medium de Sylvain et d'ici quelques jours sur le blog de RCA.
Top comments (0)