DEV Community

Cover image for DĂ©veloppeurs, faites exploser vos APIs 🧹
Jean-Phi Baconnais for Zenika

Posted on • Updated on

DĂ©veloppeurs, faites exploser vos APIs 🧹

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
Enter fullscreen mode Exit fullscreen mode

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>
Enter fullscreen mode Exit fullscreen mode

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"))
Enter fullscreen mode Exit fullscreen mode

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 :

Rapport gatling

Rapport gatling next

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.

Pipeline GitLab CI

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);
Enter fullscreen mode Exit fullscreen mode

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.

Statistics Gatling

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.

Rapport Gatling - Graph nombre de requetes

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.

Rapport Gatling - global information

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.

Board Grafana

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)