Vous avez déployé un endpoint. Il fonctionne dans votre navigateur. Mais vous n'avez aucune idée de ce qui se passe lorsque 400 personnes l'appellent simultanément. La latence reste-t-elle stable, ou le quatre-vingt-dix-neuvième centile explose-t-il ? La machine peut-elle gérer 1 000 requêtes par seconde, ou s'effondre-t-elle à 300 ?
wrk répond à ces questions. C'est un petit outil en ligne de commande qui envoie beaucoup de trafic HTTP à une URL et indique la vitesse à laquelle le serveur a répondu sous cette charge.
Qu'est-ce que wrk et quand l'utiliser
wrk est un outil moderne de benchmarking HTTP. Il génère de la charge à partir d'une seule machine multi-cœur et mesure la latence et le taux de requêtes renvoyés par le serveur. Il utilise le multithreading et une boucle d'événements évolutive (epoll sur Linux, kqueue sur macOS), de sorte qu'une seule instance peut générer beaucoup de trafic sans avoir besoin d'une flotte de machines de charge.
Utilisez wrk lorsque vous souhaitez obtenir des chiffres de performance bruts :
- Combien de requêtes par seconde ce endpoint peut-il soutenir ?
- À quoi ressemble la latence à la médiane par rapport à la queue de distribution ?
- Le serveur tient-il le coup sur une période prolongée, ou se dégrade-t-il après une minute ?
wrk est un outil de benchmarking, pas une suite de tests. Il mesure la vitesse. Il ne vérifie pas que le corps JSON est correct, que le code de statut est 200, ou que le contrat d'API a été respecté. Gardez cette distinction à l'esprit. Nous y reviendrons vers la fin, car cela change la façon dont vous intégrez wrk dans un véritable workflow de test. Si vous voulez d'abord avoir une vue d'ensemble, ce guide sur le test de charge d'API couvre les concepts que wrk met en pratique.
Installation de wrk
macOS
Homebrew propose un binaire précompilé, ce qui est le chemin le plus simple :
brew install wrk
Ceci est important sur Apple Silicon. La compilation à partir des sources peut rencontrer des problèmes avec LuaJIT ARM64, le binaire Homebrew vous épargne donc les tracas.
Linux (compilation à partir des sources)
Il n'y a pas de package apt officiel, vous devez donc le compiler. Installez d'abord la chaîne d'outils et les en-têtes OpenSSL :
sudo apt-get install build-essential libssl-dev git -y
Ensuite, clonez et compilez :
git clone https://github.com/wg/wrk.git
cd wrk
make
Ceci produit un exécutable wrk dans le répertoire actuel. Déplacez-le dans votre PATH afin de pouvoir l'appeler depuis n'importe où :
sudo cp wrk /usr/local/bin
Confirmez qu'il fonctionne :
wrk --version
La commande de base
Voici la forme de chaque exécution de wrk :
wrk -t12 -c400 -d30s http://127.0.0.1:8080/index.html
Quatre choses se passent. Analysons les drapeaux (options).
-t, --threadsdéfinit le nombre de threads du système d'exploitation que wrk lance. Un bon point de départ est un thread par cœur de CPU. Ici, c'est 12.-c, --connectionsdéfinit le nombre total de connexions HTTP maintenues ouvertes sur tous les threads. Ici, 400 connexions sont réparties sur les 12 threads. Les connexions sont la façon dont vous simulez des clients concurrents.-d, --durationdéfinit la durée d'exécution du test. Accepte des valeurs comme30s,2m, ou2h. Ici, c'est 30 secondes.
Deux autres drapeaux (options) que vous utiliserez constamment :
--latencyaffiche une répartition détaillée des percentiles de latence. Activez-le presque à chaque fois. Les moyennes masquent la latence de queue, et c'est généralement la queue qui pénalise les utilisateurs.--timeoutenregistre une requête comme ayant expiré si aucune réponse n'arrive dans la fenêtre que vous avez définie, par exemple--timeout 2s. Sans cela, les réponses lentes peuvent fausser vos chiffres de latence.
Une exécution que vous utiliserez souvent ressemble à ceci :
wrk -t8 -c200 -d30s --latency http://localhost:3000/api/users
Huit threads, 200 connexions, 30 secondes, avec la distribution complète de la latence affichée à la fin.
Lecture de la sortie
wrk affiche un rapport compact. Voici un exemple réel d'exécution contre un petit service :
Running 5s test @ http://10.135.232.163:3000
2 threads and 5 connections
Thread Stats Avg Stdev Max +/- Stdev
Latency 3.82ms 2.64ms 26.68ms 85.81%
Req/Sec 550.90 202.40 0.98k 68.00%
5494 requests in 5.01s, 1.05MB read
Requests/sec: 1096.54
Transfer/sec: 215.24KB
Lisez-le de bas en haut, car les deux dernières lignes sont les informations principales.
Requêtes/sec est le débit : combien de requêtes le serveur a complétées par seconde en moyenne. Ici, c'est 1 096. C'est le chiffre que vous comparez entre les exécutions et entre les changements de code.
Transfert/sec est la bande passante : la quantité de données déplacées par seconde. Utile lorsque les charges utiles sont importantes ou si vous soupçonnez d'être limité par la bande passante plutôt que par le CPU.
Maintenant, le tableau Statistiques des threads, qui décrit la distribution, pas seulement la moyenne :
- Ligne Latence : la latence moyenne était de 3,82 ms, mais l'écart-type était de 2,64 ms et le maximum a atteint 26,68 ms. Un maximum élevé par rapport à la moyenne est un signal. Certaines requêtes sont lentes même si la plupart sont rapides.
- Ligne Req/Sec : le taux de requêtes par thread, là encore avec sa dispersion.
La colonne +/- Stdev vous indique le pourcentage d'échantillons qui se situent à l'intérieur d'un écart-type. Des pourcentages plus faibles signifient une dispersion plus large et moins prévisible.
La ligne 5494 requests in 5.01s confirme le volume total que l'exécution a réellement généré.
Lorsque vous ajoutez --latency, wrk affiche un bloc de percentiles afin que vous puissiez voir directement la queue de distribution :
Latency Distribution
50% 3.21ms
75% 4.86ms
90% 7.09ms
99% 14.13ms
Le 99e centile est le chiffre à surveiller. Si 99 % des requêtes se terminent en 14 ms mais que votre moyenne est de 3,82 ms, un utilisateur sur cent attend bien plus longtemps que ce que la moyenne suggère. Les moyennes mentent sur les queues de distribution. Les percentiles non.
Envoyer des requêtes POST et des en-têtes personnalisés avec un script Lua
Par défaut, wrk envoie des requêtes GET. Pour envoyer un POST, ajouter un corps de requête ou définir des en-têtes personnalisés, vous passez un script Lua avec -s.
Créez un fichier nommé post.lua :
wrk.method = "POST"
wrk.body = '{"name": "Ada", "role": "engineer"}'
wrk.headers["Content-Type"] = "application/json"
Trois champs font le travail. wrk.method définit le verbe HTTP. wrk.body définit le corps de la requête. wrk.headers est un tableau où chaque clé est un nom d'en-tête.
Exécutez-le en pointant -s vers le script :
wrk -t4 -c100 -d30s -s post.lua --latency http://localhost:3000/api/users
Pour un POST encodé sous forme de formulaire au lieu de JSON, le dépôt wrk fournit cet exemple exact :
wrk.method = "POST"
wrk.body = "foo=bar&baz=quux"
wrk.headers["Content-Type"] = "application/x-www-form-urlencoded"
Vous pouvez également définir des en-têtes avec le drapeau -H pour des cas plus simples, sans script :
wrk -t4 -c100 -d30s -H "Authorization: Bearer TOKEN123" --latency http://localhost:3000/api/protected
Utilisez -H pour un ou deux en-têtes. Utilisez un script Lua lorsque vous avez besoin d'un corps de requête, d'une méthode autre que GET, ou d'une logique par requête.
Les limites : wrk ne vérifie pas la correction
Voici la partie que les gens oublient. wrk vous indique à quelle vitesse le serveur a répondu. Il ne vous dit pas si la réponse était correcte.
Dirigez wrk vers un endpoint qui renvoie un code HTTP 500 à chaque requête, et vous obtiendrez un rapport d'apparence propre avec un nombre élevé de requêtes par seconde. wrk compte un échange HTTP complété. Il n'effectue pas d'assertion sur le code de statut, ne valide pas le corps de la réponse par rapport à un schéma, et ne confirme pas que l'API a fait ce qu'elle était censée faire. Les erreurs peuvent même sembler rapides, car un serveur qui rejette les requêtes tôt fait moins de travail par requête.
Donc, wrk répond à la question « est-ce assez rapide sous charge ? » Il ne peut pas répondre à « est-ce correct ? » Les deux questions sont importantes et nécessitent des outils différents. Un chiffre de charge sur un endpoint défectueux est un chiffre auquel vous ne devriez pas faire confiance. C'est précisément pourquoi les équipes associent un outil de benchmarking à une suite de tests fonctionnels. L'un prouve la vitesse. L'autre prouve le comportement.
Où Apidog et les tests fonctionnels s'intègrent
Le workflow propre se compose de deux couches, exécutées dans l'ordre.
Premièrement, validez le comportement. Avant de vous soucier de la rapidité d'un endpoint, confirmez qu'il est correct. Dans Apidog, vous construisez des scénarios de test qui envoient de vraies requêtes et effectuent des assertions sur ce qui est renvoyé : codes de statut, champs JSON, schéma de réponse et logique métier. Vous pouvez enchaîner des requêtes, passer des données entre les étapes et exécuter le même scénario dans différents environnements. C'est cette couche qui détecte l'erreur 500 que wrk mesurerait joyeusement.
Ensuite, mesurez le débit. Une fois le comportement vérifié, exécutez wrk sur les mêmes endpoints pour voir comment ils tiennent le coup sous concurrence et charge soutenue. Apidog dispose également de tests de performance intégrés si vous préférez garder les tests fonctionnels et de charge au même endroit, mais wrk est un excellent outil dédié au benchmarking en ligne de commande pur.
La couche fonctionnelle s'exécute en intégration continue (CI), pas seulement sur votre ordinateur portable. L'interface CLI d'Apidog est sans interface graphique (headless), elle peut donc être intégrée à n'importe quelle étape de pipeline pouvant exécuter Node. Installez-la :
npm install -g apidog-cli
Ensuite, exécutez un scénario ou une suite de tests enregistrés par ID :
apidog run \
--access-token "$APIDOG_ACCESS_TOKEN" \
-t <scenarioOrSuiteId> \
-e <environmentId> \
-r cli,html,junit
-t est l'ID du scénario, du dossier ou de la suite à exécuter. -e est l'ID de l'environnement. -r sélectionne les formats de rapport, un ou plusieurs parmi cli, html, json et junit. La sortie JUnit s'intègre directement dans la plupart des systèmes CI pour le filtrage succès/échec. Pour les exécutions basées sur les données, ajoutez -d (ou --iteration-data) avec un chemin de fichier ou un ID de données de test pour itérer le même scénario sur plusieurs lignes d'entrée.
L'interface CLI exécute les scénarios et suites Apidog enregistrés. Elle est sans interface graphique (headless), ce n'est pas un expéditeur de requêtes interactif, et ce n'est pas un générateur de charge. C'est la porte de la correction. wrk est l'indicateur de vitesse. Exécutez la porte de la correction dans votre pipeline (consultez ce guide pas à pas CLI CI/CD ou le guide GitHub Actions pour la configuration à copier-coller), puis effectuez le benchmarking avec wrk lorsque vous avez besoin des chiffres de débit. La référence complète de la CLI couvre le reste des drapeaux (options).
FAQ
Quelle est la différence entre wrk et ab (ApacheBench) ? Les deux envoient une charge HTTP et rapportent le nombre de requêtes par seconde. wrk est multithreadé et utilise une boucle d'événements, il génère donc plus de charge à partir d'une seule machine et gère mieux la concurrence élevée. ab est monotâche (single-threaded). Pour une charge lourde à partir d'une seule machine, wrk est généralement plus performant. Aucun des deux ne vérifie la correction de la réponse.
Combien de threads et de connexions dois-je utiliser ? Commencez avec un thread par cœur de CPU, et définissez les connexions au niveau de concurrence que vous souhaitez simuler. Si vous avez 8 cœurs et que vous voulez simuler 200 clients concurrents, essayez -t8 -c200. Surveillez la machine cliente. Si wrk lui-même est limité par le CPU, vos chiffres reflètent la limite du générateur de charge, et non celle du serveur. Augmentez le nombre de connexions jusqu'à ce que le débit cesse d'augmenter.
wrk peut-il tester des endpoints HTTPS ? Oui. Dirigez-le vers une URL https:// et wrk gère le TLS. C'est pourquoi la compilation Linux nécessite libssl-dev. Les handshakes TLS ajoutent un coût CPU aux deux extrémités, attendez-vous donc à un débit brut plus faible pour HTTPS que pour HTTP simple.
wrk valide-t-il le corps de la réponse ou le code de statut ? Non. wrk compte les échanges HTTP complétés et mesure le temps. Il n'effectue pas d'assertion sur les codes de statut ou les corps de réponse, donc un endpoint renvoyant des erreurs peut toujours afficher un nombre élevé de requêtes par seconde. Utilisez une suite de tests fonctionnels, telle qu'une exécutée via l'interface CLI d'Apidog, pour vérifier la correction, puis utilisez wrk pour le débit.
Combien de temps doit durer un test de charge ? Assez longtemps pour dépasser les effets de démarrage comme les caches froids et la compilation JIT. Quelques secondes suffisent pour une vérification rapide, mais 30 secondes à quelques minutes donnent des chiffres plus stables et révèlent une dégradation qui n'apparaît que sous une charge soutenue. Utilisez -d30s comme valeur par défaut raisonnable et prolongez-la lorsque vous recherchez des fuites lentes.
