Optimisation: configuration du serveur Web Nginx pour améliorer les performances RPS dans l'API HTTP



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

Sources






All Articles