Avant de mettre à l'échelle et de faire évoluer votre infrastructure, la première étape consiste à vous assurer que les ressources sont utilisées correctement et que la configuration de l'application ne gêne pas ses performances. L'objectif principal de l'équipe d'ingénierie est d'assurer le fonctionnement continu et ininterrompu de tout système conçu et déployé avec un minimum de ressources.
Nous avons rencontré le problème ci-dessus où notre système déployé était utilisé quotidiennement par un million d'utilisateurs qui se connectaient par rafales de temps en temps. Cela signifie que le déploiement de plusieurs serveurs ou leur mise à l'échelle ne sera pas la meilleure solution dans cette situation.
Cet article concerne le réglage de Nginx pour améliorer les performances, c'est-à-dire pour augmenter le RPS (requêtes par seconde) dans l'API HTTP. J'ai essayé de vous parler de l'optimisation que nous avons appliquée dans le système déployé pour traiter des dizaines de milliers de requêtes par seconde sans gaspiller une énorme quantité de ressources.
Plan d'action: vous devez exécuter l'API HTTP (écrite en Python à l'aide de flask), mandatée avec Nginx; une bande passante élevée est requise. Le contenu de l'API changera tous les jours.
optimisation
nom
processus pour obtenir le meilleur résultat; l'utilisation la plus efficace d'une situation ou d'une ressource.
Nous avons utilisé le superviseur pour démarrer WSGI Server avec les configurations suivantes:
- Gunicorn avec les travailleurs Meinheld
- Nombre de travailleurs: nombre de processeurs * 2 + 1
- Liez le socket à une adresse Unix au lieu d'une adresse IP, cela augmentera légèrement la vitesse .
La commande superviseur ressemble à ceci:
gunicorn api:app --workers=5 --worker-
class=meinheld.gmeinheld.MeinheldWorker --bind=unix:api.sock
Nous avons essayé d'optimiser la configuration de Nginx et vérifié ce qui fonctionnait le mieux pour nous.
Pour évaluer les performances de l'API, nous avons utilisé wrk avec la commande suivante:
wrk -t20 -c200 -d20s http://api.endpoint/resource
Configuration par défaut
Nous avons d'abord effectué des tests de charge de l'API sans aucune modification et avons obtenu les statistiques suivantes:
Running 20s test @ http://api.endpoint/resource
20 threads and 200 connections
Thread Stats Avg Stdev Max +/- Stdev
Latency 192.48ms 274.78ms 1.97s 87.18%
Req/Sec 85.57 29.20 202.00 72.83%
33329 requests in 20.03s, 29.59MB read
Socket errors: connect 0, read 0, write 0, timeout 85
Requests/sec: 1663.71
Transfer/sec: 1.48MB
Mettre à jour la configuration par défaut
Mettons à jour la configuration par défaut de Nginx, c'est-à-dire nginx.conf dans /etc/nginx/nginx.conf
worker_processes auto;
#or should be equal to the CPU core, you can use `grep processor /proc/cpuinfo | wc -l` to find; auto does it implicitly.
worker_connections 1024;
# default is 768; find optimum value for your server by `ulimit -n`
access_log off;
# to boost I/O on HDD we can disable access logs
# this prevent nginx from logging every action in a log file named `access.log`.
keepalive_timeout 15;
# default is 65;
# server will close connection after this time (in seconds)
gzip_vary on;
gzip_proxied any;
gzip_comp_level 2;
gzip_buffers 16 8k;
gzip_http_version 1.1;
gzip_min_length 256;
gzip_types text/plain text/css application/json application/x-javascript text/xml application/xml application/xml+rss text/javascript;
# reduces the data that needs to be sent over the network
nginx.conf (/etc/nginx/nginx.conf)
Après les modifications, nous exécutons la vérification de la configuration:
sudo nginx -t
Si la vérification réussit, vous pouvez redémarrer Nginx pour refléter les modifications:
sudo service nginx restart
Avec cette configuration, nous avons effectué des tests de charge de l'API et obtenu le résultat suivant:
Running 20s test @ http://api.endpoint/resource
20 threads and 200 connections
Thread Stats Avg Stdev Max +/- Stdev
Latency 145.80ms 237.97ms 1.95s 89.51%
Req/Sec 107.99 41.34 202.00 66.09%
42898 requests in 20.03s, 39.03MB read
Socket errors: connect 0, read 0, write 0, timeout 46
Non-2xx or 3xx responses: 2
Requests/sec: 2141.48
Transfer/sec: 1.95MB
Ces configurations réduisaient les délais et augmentaient le RPS (requêtes par seconde), mais pas beaucoup.
Ajout de cache Nginx
Étant donné que, dans notre cas, le contenu du point de terminaison sera mis à jour à un intervalle d'un jour, cela crée un environnement approprié pour la mise en cache des réponses API.
Mais l'ajout de cache le rend invalide ... c'est l'une des deux difficultés ici.
En informatique, il n'y a que deux complications: invalider le cache et nommer les choses. - Phil Carlton
Nous choisissons une solution simple pour vider le répertoire de cache à l'aide d'un cronjob après la mise à jour du contenu sur le système en aval.
Ensuite, Nginx fera tout le travail acharné, mais maintenant nous devons nous assurer que Nginx est prêt à 100%!
Pour ajouter la mise en cache à Nginx, vous devez ajouter plusieurs directives au fichier de configuration Nginx.
Avant cela, nous devons créer un répertoire pour stocker les données du cache:
sudo mkdir -p /data/nginx/cache
Modifications de la configuration de Nginx:
proxy_cache_path /data/nginx/cache keys_zone=my_zone:10m inactive=1d;
server {
...
location /api-endpoint/ {
proxy_cache my_zone;
proxy_cache_key "$host$request_uri$http_authorization";
proxy_cache_valid 404 302 1m;
proxy_cache_valid 200 1d;
add_header X-Cache-Status $upstream_cache_status;
}
...
}
Mise en cache des requêtes proxy (configuration Nginx)
Après ce changement de configuration, nous avons testé le chargement de l'API et obtenu le résultat suivant:
Running 20s test @ http://api.endpoint/resource
20 threads and 200 connections
Thread Stats Avg Stdev Max +/- Stdev
Latency 6.88ms 5.44ms 88.91ms 81.36%
Req/Sec 1.59k 500.04 2.95k 62.50%
634405 requests in 20.06s, 589.86MB read
Requests/sec: 31624.93
Transfer/sec: 29.40MB
Ainsi, nous avons obtenu une augmentation de presque 19 fois des performances en ajoutant la mise en cache.
Remarque d'un expert Timeweb :
Il est important de se rappeler que la mise en cache des requêtes qui écrivent dans la base de données entraînera une réponse mise en cache, mais aucune écriture dans la base de données.
Cache Nginx dans la RAM (Random Access Memory)
Allons plus loin! Actuellement, nos données de cache sont stockées sur disque. Et si nous sauvegardions ces données dans la RAM? Dans notre cas, les données de réponse sont limitées et peu importantes.
Donc, vous devez d'abord créer un répertoire dans lequel le cache RAM sera monté:
sudo mkdir -p /data/nginx/ramcache
Pour monter le répertoire créé dans la RAM à l'aide de tmpfs , utilisez la commande:
sudo mount -t tmpfs -o size=256M tmpfs /data/nginx/ramcache
Cela monte / data / nginx / ramcache dans la RAM, allouant 256 Mo.
Si vous pensez que vous souhaitez désactiver le cache RAM, exécutez simplement la commande:
sudo umount /data/nginx/ramcache
Pour recréer automatiquement le répertoire de cache dans la RAM après le redémarrage, nous devons mettre à jour le fichier / etc / fstab . Ajoutez-y la ligne suivante:
tmpfs /data/nginx/ramcache tmpfs defaults,size=256M 0 0
Remarque: Nous devons également enregistrer la valeur proxy_cache_path avec le chemin vers ramcache ( / data / nginx / ramcache ).
Après avoir mis à jour la configuration, nous avons à nouveau effectué des tests de charge de l'API et obtenu le résultat suivant:
Running 20s test @ http://api.endpoint/resource
20 threads and 200 connections
Thread Stats Avg Stdev Max +/- Stdev
Latency 5.57ms 5.69ms 277.76ms 92.94%
Req/Sec 1.98k 403.94 4.55k 71.77%
789306 requests in 20.04s, 733.89MB read
Requests/sec: 39387.13
Transfer/sec: 36.62MB
Le stockage du cache dans la RAM a entraîné une amélioration significative de près de 23 fois .
Journal d'accès tamponné
Nous conservons un journal des accès aux applications mandatées, mais vous pouvez d'abord enregistrer le journal dans un tampon et ensuite seulement l'écrire sur le disque:
- si la ligne suivante du journal ne rentre pas dans le tampon
- si les données du tampon sont plus anciennes que celles spécifiées dans le paramètre flush .
Cette procédure réduira la fréquence d'enregistrement effectuée à chaque demande. Pour ce faire, il suffit d'ajouter les paramètres buffer et flush avec la valeur appropriée dans la directive access_log :
location / {
...
access_log /var/log/nginx/fast_api.log combined buffer=256k flush=10s;
error_log /var/log/nginx/fast_api.err.log;
}
Journal de la mémoire tampon avant l'écriture sur le disque
Ainsi, selon la configuration ci-dessus, les journaux d'accès seront initialement mis en mémoire tampon et enregistrés sur le disque uniquement lorsque la mémoire tampon atteint 256 Ko ou que les données mises en mémoire tampon ont plus de 10 secondes.
Remarque: Le nom est log_format combiné ici .
Après des tests de charge répétés, nous avons obtenu le résultat suivant:
Running 20s test @ http://api.endpoint/resource
20 threads and 200 connections
Thread Stats Avg Stdev Max +/- Stdev
Latency 4.21ms 3.19ms 84.83ms 83.84%
Req/Sec 2.53k 379.87 6.02k 77.05%
1009771 requests in 20.03s, 849.31MB read
Requests/sec: 50413.44
Transfer/sec: 42.40MB
Cette configuration a considérablement augmenté le nombre de requêtes par seconde, environ 30 fois par rapport à l'étape initiale.
Production
Dans cet article, nous avons discuté du processus d'optimisation de la configuration de Nginx pour améliorer les performances RPS. Le RPS a été augmenté de 1663 à ~ 50413 ( une augmentation d'environ 30 fois ), ce qui offre un débit élevé. En ajustant les paramètres par défaut, vous pouvez améliorer les performances du système.
Terminons l'article par une citation:
Faites-le fonctionner en premier. Alors fais-le bien. Puis optimisez. - Kent Beck