Vous avez déployé un endpoint. Il renvoie le bon JSON dans Postman. Mais vous n'avez aucune idée de ce qui se passe lorsque 200 clients l'atteignent simultanément. Gère-t-il 500 requêtes par seconde, ou la latence s'effondre-t-elle à 50 ? ApacheBench répond à cette question en une seule commande.
ApacheBench, appelé ab, est un outil en ligne de commande qui envoie un nombre fixe de requêtes HTTP à une seule URL et rapporte le débit et la latence. Il est fourni avec le serveur HTTP Apache et existe depuis des décennies. Il est petit, rapide à exécuter, et vous donne une idée rapide de la charge qu'un endpoint peut supporter.
Ce guide couvre l'installation d'`ab`, l'exécution d'un test de base, le test des endpoints POST, la lecture de la sortie, et quand `ab` n'est plus l'outil approprié. Chaque commande ici correspond à un drapeau documenté dans la référence officielle d'Apache ab.
Qu'est-ce qu'ApacheBench et d'où il vient
ab est un client de benchmarking fourni avec le serveur HTTP Apache. Le nom est l'abréviation d'ApacheBench. Il ouvre des connexions à une URL, envoie des requêtes aussi rapidement que votre paramètre de concurrence le permet, chronomètre chaque réponse et affiche un résumé.

Il mesure bien une chose : combien de requêtes par seconde un seul endpoint peut servir, et comment le temps de réponse est distribué sous charge. C'est le débit et la latence pour une URL donnée.
Ce qu'il ne fait pas est tout aussi important. ab ne vérifie pas si le corps de la réponse est correct. Il ne valide pas de schéma ni n'affirme de valeur pour un champ. Il n'exécute pas de flux en plusieurs étapes comme se connecter, puis récupérer, puis mettre à jour. Il cible une URL et compte. Gardez cette portée à l'esprit et ab restera utile. Attendez-vous à plus et vous serez déçu.
Si vous voulez une vue plus large de la discipline dans laquelle `ab` s'inscrit, consultez ce qu'est le test de charge d'API et pourquoi le temps de réponse d'une API est important.
Installation d'ab
ab est fourni avec les utilitaires Apache. Le nom du paquet diffère selon la plateforme.
Sur Debian et Ubuntu, installez le paquet apache2-utils :
sudo apt-get update
sudo apt-get install -y apache2-utils
Sur CentOS, RHEL et Fedora, le paquet est httpd-tools :
# CentOS 7
sudo yum install -y httpd-tools
# Fedora and CentOS 8+
sudo dnf install -y httpd-tools
Sur macOS, ab est déjà présent car le système est livré avec Apache. Vérifiez-le :
ab -V
Vous devriez voir une ligne de version comme Version 2.3. Si la commande est manquante sur macOS, installez-la via la formule Apache de Homebrew. Une fois que ab -V affiche une version, vous êtes prêt.
Exécution d'un test de charge de base
Les deux options que vous utiliserez à chaque fois sont -n et -c.
-n requestsdéfinit le nombre total de requêtes à effectuer. La valeur par défaut est une seule requête, ce qui ne vous dit rien, alors définissez toujours cette option.-c concurrencydéfinit le nombre de requêtes exécutées simultanément. La valeur par défaut est une, ce qui signifie entièrement sériel.
Voici 1000 requêtes, 50 à la fois, contre un endpoint JSON :
ab -n 1000 -c 50 https://api.example.com/v1/users
Notez le chemin de fin. ab a besoin d'une URL complète avec un chemin. Si vous le dirigez vers un hôte nu sans chemin, il affichera une erreur. Pour la racine, utilisez un slash final : https://api.example.com/.
Les clients réels réutilisent les connexions TCP au lieu d'en ouvrir une nouvelle pour chaque requête. Ajoutez -k pour activer HTTP KeepAlive afin qu'`ab` réutilise les connexions au sein d'une session :
ab -n 1000 -c 50 -k https://api.example.com/v1/users
L'exécution avec -k est généralement plus proche de la façon dont se comporte un navigateur ou un client API bien conçu, car elle évite de payer le coût d'établissement de la connexion à chaque requête. Comparez les deux chiffres pour voir quelle part de votre latence est due aux frais généraux de connexion.
Vous pouvez également limiter l'exécution par temps plutôt que par nombre. -t définit un nombre maximal de secondes et implique -n 50000 en interne, de sorte que le test s'arrête à la première limite atteinte :
ab -t 30 -c 50 -k https://api.example.com/v1/users
Cela s'exécute pendant un maximum de 30 secondes avec une concurrence de 50. Pratique lorsque vous souhaitez une lecture de durée fixe plutôt qu'un nombre fixe.
Tester un endpoint POST
La plupart du travail d'API n'est pas de type GET. Pour tester une requête POST, placez le corps de la requête dans un fichier et passez-le avec -p. Vous devez également définir le type de contenu avec -T, sinon le serveur rejettera le corps.
Créez la charge utile :
cat > payload.json <<'EOF'
{"name": "Ada Lovelace", "email": "ada@example.com"}
EOF
Ensuite, envoyez-la :
ab -n 500 -c 25 -k \
-p payload.json \
-T application/json \
https://api.example.com/v1/users
L'option -p spécifie le fichier contenant le corps de la requête POST. L'option -T définit l'en-tête Content-Type. Le type de contenu par défaut est text/plain, ce qui n'est presque jamais ce qu'une API JSON souhaite, alors définissez explicitement -T application/json.
Si votre endpoint nécessite une authentification ou d'autres en-têtes, ajoutez-les avec -H. Répétez l'option pour chaque en-tête :
ab -n 500 -c 25 -k \
-p payload.json \
-T application/json \
-H "Authorization: Bearer YOUR_TOKEN" \
https://api.example.com/v1/users
Pour un rappel sur la construction manuelle de corps de requêtes JSON, consultez comment envoyer des données JSON avec curl. Le même corps fonctionne comme un fichier de charge utile pour `ab`.
Lecture de la sortie
Une exécution affiche un bloc de chiffres. Voici un exemple raccourci :
Concurrency Level: 50
Time taken for tests: 4.212 seconds
Complete requests: 1000
Failed requests: 0
Non-2xx responses: 0
Requests per second: 237.42 [#/sec] (mean)
Time per request: 210.598 [ms] (mean)
Time per request: 4.212 [ms] (mean, across all concurrent requests)
Transfer rate: 142.31 [Kbytes/sec] received
Connection Times (ms)
min mean[+/-sd] median max
Connect: 5 18 9.4 16 64
Processing: 22 189 41.2 182 310
Waiting: 21 177 39.8 171 295
Total: 31 207 42.7 201 338
Percentage of the requests served within a certain time (ms)
50% 201
66% 221
75% 236
90% 268
95% 291
99% 324
100% 338 (longest request)
Lisez d'abord ces champs :
- Requêtes par seconde est votre titre de débit. C'est le nombre de requêtes divisé par le temps total. Plus c'est élevé, mieux c'est.
- Requêtes échouées et Réponses non-2xx vous indiquent si le serveur a réellement géré la charge ou a commencé à générer des erreurs. Un nombre élevé de requêtes par seconde ne signifie rien si la moitié des réponses sont des 500. Vérifiez toujours ces deux éléments avant de faire confiance au chiffre de débit.
- Temps par requête apparaît deux fois. La première ligne est le temps moyen qu'un seul utilisateur attend (concurrence multipliée par le temps total divisé par les requêtes). La deuxième ligne, étiquetée « across all concurrent requests », est l'écart moyen entre les requêtes terminées. Le premier chiffre est celui que les utilisateurs ressentent.
Le tableau Pourcentage des requêtes traitées dans un certain temps est la partie la plus utile. C'est une répartition par percentiles. La ligne 50 % est votre médiane. Les lignes 95 % et 99 % montrent la latence des requêtes les plus lentes. Dans l'exemple, la moitié des requêtes se sont terminées en 201 ms, mais 1 % ont pris 324 ms ou plus. La latence de la queue (tail latency) est ce qui pénalise les utilisateurs réels, alors surveillez les 95e et 99e percentiles plus que la moyenne.
Vous voulez les chiffres bruts par requête pour un graphique ? Ajoutez -e results.csv pour écrire un fichier CSV de paires percentile-temps, ou -g results.tsv pour un fichier compatible gnuplot.
Quand ab cesse d'être le bon outil
ab est délibérément limité dans sa portée. Ses limites méritent d'être clairement énoncées afin de ne pas en abuser.
- Une URL par exécution. `ab` martèle un seul endpoint. Il ne peut pas scripté une séquence comme s'authentifier, créer une ressource, puis la lire. Les parcours utilisateur réels touchent de nombreux endpoints dans un ordre précis, et `ab` n'a aucune notion de cela.
- Aucune vérification de la justesse. `ab` compte les codes de statut et les longueurs en octets. Il ne parse pas votre JSON ni n'affirme qu'un champ est égal à une valeur attendue. Une réponse peut être structurellement incorrecte et tout de même compter comme un succès. Les chiffres de charge ne remplacent pas une suite de tests réussie.
- Gestion HTTP obsolète. Les documents officiels indiquent qu'`ab` n'implémente pas entièrement HTTP/1.x et n'accepte que certaines formes de réponses attendues. Il ne prend pas en charge HTTP/2. Pour les serveurs qui se comportent différemment sous HTTP/2, `ab` ne reflétera pas cela.
- Charge d'une seule machine. `ab` s'exécute depuis une seule machine et un seul processus. Au-delà d'une certaine concurrence, vous mesurez les limites de votre propre client plutôt que celles du serveur. La documentation avertit même que l'analyse propre à `ab` peut apparaître comme un goulot d'étranglement dans les profils.
Rien de tout cela ne rend `ab` mauvais. Cela en fait un outil spécifique : une sonde de débit rapide pour un seul endpoint. Lorsque vous avez besoin de flux scriptés, de charge distribuée ou de HTTP/2, tournez-vous vers un outil conçu à cet effet. JMeter gère les scénarios en plusieurs étapes et les exécutions distribuées, et il existe un aperçu plus large des outils de test de charge si vous comparez les options.
Où le test fonctionnel s'insère
Le débit est un axe. La justesse en est un autre, et `ab` n'y touche pas. Avant de tester la charge d'un endpoint, vous voulez savoir s'il renvoie les bonnes données avec la bonne forme dans des conditions normales. C'est du test fonctionnel et de contrat, et c'est un travail différent.
C'est là qu'une plateforme API complète gagne sa place aux côtés d'`ab`. Apidog vous permet de construire des scénarios de test avec des assertions visuelles, d'enchaîner des requêtes en flux multi-étapes, et de valider les réponses par rapport à votre schéma, des choses qu'`ab` ne peut pas faire par conception. Vous sauvegardez ces scénarios une fois et les exécutez n'importe où.
Pour l'intégration continue, l'interface en ligne de commande (CLI) d'Apidog exécute vos scénarios sauvegardés sans interface graphique. Installez-la avec Node :
npm install -g apidog-cli
Exécutez ensuite un scénario ou une suite sauvegardé(e) contre un environnement choisi, en émettant des rapports que votre CI peut archiver :
apidog run \
--access-token "$APIDOG_ACCESS_TOKEN" \
-t <scenarioOrSuiteId> \
-e <environmentId> \
-r cli,html,junit
L'option -t cible un scénario, un dossier ou une suite sauvegardé(e) par son identifiant. L'option -e sélectionne l'environnement. L'option -r sélectionne un ou plusieurs générateurs de rapports parmi cli, html, json et junit. Pour les exécutions basées sur des données, ajoutez -d (ou --iteration-data) avec un chemin de fichier de données ou un identifiant de données de test. L'interface en ligne de commande est sans interface graphique et exécute les scénarios sauvegardés, elle s'adapte donc à toute étape de CI qui peut exécuter Node. Ce n'est pas un émetteur de requêtes ni un générateur de charge, il exécute les tests fonctionnels qu'`ab` n'a jamais été destiné à exécuter.
Une bonne configuration utilise les deux. Exécutez les scénarios Apidog pour prouver que l'endpoint est correct. Exécutez `ab` pour prouver qu'il est assez rapide. Pour le côté justesse, consultez le guide de test de performance Apidog et le tutoriel général sur les tests de performance API.
FAQ
ApacheBench est-il uniquement pour les serveurs Apache ?
Non. Malgré son nom, ab envoie des requêtes HTTP et HTTPS simples à n'importe quel serveur. Il fonctionne avec Nginx, Node, Go, Python, ou tout hôte qui communique en HTTP. Le lien avec Apache est uniquement que l'outil est livré avec le serveur HTTP Apache.
Quelle concurrence et quel nombre de requêtes dois-je utiliser ?
Commencez bas et augmentez progressivement. Essayez -n 1000 -c 10, puis augmentez -c à 25, 50, 100, et observez où les requêtes par seconde cessent d'augmenter et où les requêtes échouées commencent à apparaître. Ce point d'inflexion est à peu près l'endroit où le endpoint sature. Adaptez votre concurrence maximale au trafic réel attendu plutôt que de choisir un grand nombre rond.
Pourquoi mes requêtes échouées sont-elles élevées alors que l'API fonctionne bien ?
ab marque une requête comme échouée si la longueur de la réponse varie entre les requêtes, ce qui est normal pour le JSON dynamique. Ajoutez l'option -l pour ne plus compter les différences de longueur comme des échecs. Vérifiez ensuite la ligne des réponses non-2xx pour voir s'il y a de réelles erreurs en dessous.
ab peut-il tester un endpoint qui nécessite un jeton de connexion ?
Oui, si vous avez déjà un jeton. Passez-le avec -H "Authorization: Bearer VOTRE_JETON". Ce qu'`ab` ne peut pas faire, c'est se connecter d'abord pour obtenir un nouveau jeton, puis l'utiliser. C'est un flux en plusieurs étapes, et vous avez besoin d'un outil basé sur des scénarios comme Apidog pour cela.
ab prend-il en charge HTTP/2 ?
Non. ab utilise HTTP/1.x et, selon la documentation officielle, ne l'implémente même pas entièrement. Si le comportement de votre serveur sous HTTP/2 est important, utilisez plutôt un outil de charge avec prise en charge HTTP/2.
