Test de performance précis avec `wrk`
API7.ai
November 25, 2022
Dans cet article, nous allons parler des tests de performance. Cette partie n'est pas spécifique à OpenResty mais s'applique à d'autres services backend.
Les tests de performance sont très répandus, et lorsque nous livrons des produits, ils sont accompagnés de métriques de performance, telles que le QPS, le TPS, la latence, le nombre d'utilisateurs supportés simultanément, etc. Pour les projets open source, nous effectuons également un test de performance avant de publier une version pour la comparer à la précédente et voir s'il y a une baisse significative. Il existe également des sites neutres qui publient des données de performance comparatives de produits similaires. Je dois dire que les tests de performance nous sont très proches.
Alors, comment réaliser des tests de performance scientifiques et rigoureux ?
Outils de test de performance
Pour bien faire, il faut d'abord utiliser un bon outil. Choisir un bon outil de test de performance, c'est déjà la moitié du succès.
L'outil Apache Benchmark, également connu sous le nom de ab
, que vous devriez connaître, est sans doute l'outil de test de performance le plus simple, mais malheureusement, il n'est pas très utile. En effet, le côté serveur actuel est développé sur la base d'I/O concurrent et asynchrone, dont les performances ne sont pas mauvaises. Le ab
ne tire pas parti des multi-cœurs de la machine et les requêtes générées ne sont pas suffisamment stressantes. Dans ce cas, les résultats obtenus avec ab
ne sont pas réels.
Par conséquent, nous pouvons choisir un critère pour l'outil de test de charge : l'outil lui-même doit avoir des performances solides et être capable de générer une charge suffisamment importante pour stresser le programme côté serveur.
Bien sûr, vous pouvez également dépenser plus d'argent pour démarrer de nombreux clients de test de charge et les transformer en un système de test de charge distribué. Mais n'oubliez pas que la complexité augmente également.
Revenons à la pratique d'OpenResty, notre outil de test de performance recommandé est wrk
. Tout d'abord, pourquoi le choisissons-nous ?
Premièrement, wrk
répond aux critères de sélection des outils. La charge générée par wrk
sur une seule machine peut facilement permettre à NGINX d'atteindre 100 % d'utilisation du CPU, sans parler d'autres applications côté serveur.
Deuxièmement, wrk
a beaucoup de similitudes avec OpenResty. wrk
n'est pas un projet open source écrit à partir de zéro ; il repose sur les épaules de LuaJIT et Redis et tire parti des ressources multi-cœurs du système pour générer des requêtes. De plus, wrk
expose une API Lua qui vous permet d'intégrer vos scripts Lua pour personnaliser les en-têtes et le contenu des requêtes, ce qui le rend très flexible.
Alors, comment utiliser wrk
? C'est aussi simple que de regarder l'extrait de code suivant.
wrk -t12 -c400 -d30s http://127.0.0.1:8080/index.html
Cela signifie que wrk
utilisera 12 threads, maintiendra 400 connexions longues pendant 30 secondes, pour envoyer des requêtes HTTP à l'interface API spécifiée. Bien sûr, si vous ne spécifiez pas de paramètres, wrk
démarrera par défaut deux threads et dix connexions longues.
Environnement de test
Après avoir trouvé les outils de test, nous ne pouvons pas commencer directement le test de charge. Nous devons vérifier une fois l'environnement de test. Il y a quatre éléments principaux à vérifier dans l'environnement de test, et je vais en discuter en détail.
1. Désactiver SELinux
Si vous avez un système d'exploitation CentOS/RedHat, il est recommandé de désactiver SELinux. Sinon, vous pourriez rencontrer de nombreux problèmes de permissions étranges.
Vérifions si SELinux est activé avec la commande suivante.
$ sestatus
SELinux status: disabled
S'il indique qu'il est activé (enforcing), vous pouvez le désactiver temporairement avec $ setenforce 0
; modifiez également le fichier /etc/selinux/config
pour le désactiver définitivement en changeant SELINUX=enforcing
en SELINUX=disabled
.
2. Nombre maximal de fichiers ouverts
Ensuite, vous devez vérifier le nombre maximal de fichiers ouverts actuel du système avec la commande suivante.
$ cat /proc/sys/fs/file-nr
3984 0 3255296
Le dernier nombre 3255296
ici est le nombre maximal de fichiers ouverts. Si ce nombre est petit sur votre machine, vous devez modifier le fichier /etc/sysctl.conf
pour l'augmenter.
fs.file-max = 1020000
net.ipv4.ip_conntrack_max = 1020000
net.ipv4.netfilter.ip_conntrack_max = 1020000
Après la modification, vous devez redémarrer le service système pour que cela prenne effet.
sudo sysctl -p /etc/sysctl.conf
3. Limites des processus
En plus du nombre maximal de fichiers ouverts sur le système, il y a également une limite au nombre de fichiers qu'un processus peut ouvrir, que vous pouvez vérifier avec la commande ulimit
.
$ ulimit -n
1024
Vous remarquerez que cette valeur est par défaut de 1024, une petite valeur. Comme chaque requête utilisateur correspond à un descripteur de fichier, et que les tests de charge génèrent beaucoup de requêtes, nous devons augmenter cette valeur et la changer à des millions, ce que vous pouvez faire temporairement avec la commande suivante.
ulimit -n 1024000
Vous pouvez également modifier le fichier de configuration /etc/security/limits.conf
pour le rendre permanent.
* hard nofile 1024000
* soft nofile 1024000
4. Configuration NGINX
Enfin, vous devrez apporter une petite modification à la configuration de NGINX, qui est les trois lignes de code suivantes.
events {
worker_connections 10240;
}
Cela nous permet d'augmenter le nombre de connexions par Worker. Comme la valeur par défaut est seulement de 512, ce n'est pas suffisant pour des tests de charge élevés.
Vérification avant le test de charge
À ce stade, l'environnement de test est prêt. Vous devez être impatient de commencer et de le tester, n'est-ce pas ? Vérifions une dernière fois avant de lancer le test avec wrk
. Après tout, les gens font des erreurs, il est donc essentiel de faire un test croisé.
Ce dernier test peut être divisé en deux étapes.
1. Utiliser l'outil automatisé c1000k
c1000k vient de l'auteur de SSDB
. Comme vous pouvez le voir par le nom, le but de cet outil est de vérifier si votre environnement peut répondre aux exigences de 10^6 connexions concurrentes.
L'utilisation de cet outil est également très simple. Nous démarrons un server
et un client
, correspondant au programme serveur écoutant sur le port 7000
et au programme client lançant le test de charge pour simuler le test de charge dans un environnement réel :
. /server 7000
. /client 127.0.0.1 7000
Immédiatement après, le client
envoie une requête au server
pour vérifier si l'environnement système actuel peut supporter un million de connexions concurrentes. Vous pouvez l'exécuter vous-même et voir le résultat.
2. Vérifier si le programme serveur fonctionne normalement
Si le programme côté serveur ne fonctionne pas correctement, le test de charge peut devenir un test de rafraîchissement des journaux d'erreurs ou un test de réponse 404
.
Ainsi, la dernière et la plus cruciale étape du test de l'environnement de test est de parcourir l'ensemble des tests unitaires côté serveur ou d'appeler manuellement quelques interfaces principales pour s'assurer que toutes les interfaces, les retours et les codes de réponse HTTP du test wrk
sont normaux et qu'il n'y a pas de messages de niveau erreur dans logs/error.log
.
Envoi des requêtes
D'accord, maintenant tout est prêt. Commençons le test de charge avec wrk
!
$ wrk -d 30 http://127.0.0.2:9080/hello
Running 30s test @ http://127.0.0.2:9080/hello
2 threads and 10 connections
Thread Stats Avg Stdev Max +/- Stdev
Latency 595.39us 178.51us 22.24ms 90.63%
Req/Sec 8.33k 642.91 9.46k 59.80%
499149 requests in 30.10s, 124.22MB read
Requests/sec: 16582.76
Transfer/sec: 4.13MB
Je n'ai pas spécifié de paramètres ici, donc wrk
démarrera par défaut 2 threads et 10 connexions longues. Vous n'avez pas besoin d'ajuster le nombre de threads et de connexions dans wrk
pour qu'ils soient très grands ; tant que vous pouvez amener le programme cible à atteindre 100 % d'utilisation du CPU, tout ira bien.
Mais la durée du test de charge ne doit pas être trop courte, car quelques secondes de test de charge n'ont aucun sens. Sinon, le test de charge sera probablement terminé avant que le programme serveur ne termine le rechargement à chaud. En même temps, vous devez utiliser un outil de surveillance comme top
ou htop
pour vérifier si le programme cible sur le serveur fonctionne à 100 % d'utilisation du CPU pendant le test de charge.
Phénoménalement, si le CPU est entièrement chargé et que l'utilisation du CPU et de la mémoire diminue rapidement après l'arrêt du test, alors félicitations, le test a été réalisé avec succès. Cependant, s'il y a des exceptions comme les suivantes, en tant que développeur côté serveur, vous devriez y prêter attention.
- Le CPU ne peut pas être entièrement chargé. Ce n'est pas un problème de
wrk
; cela pourrait être une limitation réseau ou une opération bloquante dans votre code. Vous pouvez le déterminer en examinant votre code ou en utilisant le graphique de flammeoff CPU
. - Le CPU est toujours entièrement chargé, même lorsque la charge s'arrête. Cela indique une boucle infinie dans le code causée par une expression régulière ou un bug de LuaJIT, que j'ai rencontré dans des environnements réels. À ce stade, vous devrez utiliser le graphique de flamme CPU pour le déterminer.
Enfin, regardons les statistiques de wrk
. Concernant ce résultat, nous nous concentrons généralement sur deux valeurs.
La première est le QPS, ou Requests/sec: 16582.76
, qui est un chiffre exact indiquant combien de requêtes sont traitées par seconde côté serveur.
La seconde est la latence : Latency 595.39us 178.51us 22.24ms 90.63%
, aussi importante que le QPS, qui reflète la vitesse de réponse du système. Par exemple, pour les applications de passerelle, nous voulons garder la latence en dessous de 1 ms.
En outre, wrk
fournit également un paramètre latency
qui imprime en détail la distribution en pourcentage de la latence, par exemple.
Latency Distribution
50% 134.00us
75% 180.00us
90% 247.00us
99% 552.00us
Cependant, les données de distribution de latence de wrk
sont inexactes car elles ajoutent artificiellement des perturbations réseau et d'outils qui amplifient la latence, ce qui nécessite votre attention particulière.
Résumé
Les tests de performance sont un travail technique ; peu de gens peuvent le faire correctement et bien. J'espère que cet article vous donnera une compréhension plus complète des tests de performance.
Enfin, je vous laisse avec une question : wrk
supporte les scripts Lua personnalisés pour faire des tests de charge, donc pouvez-vous écrire un simple script Lua basé sur sa documentation ? Cela peut être un peu difficile, mais vous comprendrez l'intention des interfaces exposées par wrk
lorsque vous l'aurez terminé.
Vous êtes invités à partager cet article avec plus de personnes, et nous progresserons ensemble.