title: Optimisez Nginx pour de meilleurs performances url: https://buzut.net/optimiser-nginx/ hash_url: b33059055c307477e43390a51f9a104a archive_date: 2024-03-13 og_image: https://d33wubrfki0l68.cloudfront.net/f87f6e0bd31d5f46d08cde68fbb8ee51bb3a9e91/969de/img/logo.png description: Nginx est réputé pour sa rapidité, mais quand votre serveur fait face à un trafic énorme, il est possible d'en tirer encore plus. Voyons comment ! favicon: https://buzut.net/img/favicon.png language: fr_FR
Nginx de part sa robustesse, sa structure minimaliste et son fonctionnement événementiel asynchrone est un serveur web plébiscité pour ses performances. Mais ce n’est pas parce qu’on a une Porsche qu’il ne faut pas tenter de la rendre encore plus puissante !
Pour des sites à fort trafic, un serveur bien optimisé signifie deux choses : des requêtes servies plus vite et un besoin en machines (scaling horizontal) inférieur. Alors pourquoi s’en priver ?
La première chose à explorer est le fichier de configuration général de Nginx : /etc/nginx/nginx.conf
. Nous allons ici définir le nombre de workers ainsi que le nombre de connexions pour chacun d’entre eux.
Concentrons nous d’abord sur worker_processes
. Cette directive spécifie le nombre total de workers à créer au démarrage de Nginx. La valeur optimale est d’en avoir un par cpu core. Si vous avez un VPS – lesquels ont souvent un vCPU avec un seul vCore – il arrive souvent que la valeur par défaut soit supérieure au nombre total de cores. Ce n’est pas très grave, néanmoins, les processus supplémentaires vont un peu se tourner les pouces… pas bien utile. Pour définir cette valeur, il nous suffit donc de déterminer le nombre total de cores :
# grep -c ^processor /proc/cpuinfo
12
Nous avons notre réponse, ici : 12.
Passons maintenant à la directive worker_connections
. Elle spécifie combien de connections simultanées chaque worker est en mesure d’établir. Étant donné qu’une connexion nécessite un file descriptor au minimum, une bonne base est d’établir ce nombre en fonction des limites de notre système. Dans le cas de Nginx en reverse proxy, il faut un file descriptor pour la connexion client et un autre vers le serveur proxifié, soit deux par connexion.
# ulimit -n
1024
Par défaut, la limite du nombre de fichiers ouverts est souvent de 1024. Selon la configuration du système, cette limite peut être très largement augmentée. Voyez mon article sur ulimit afin de modifier ces configurations.
Vous lirez peut-être dans certains articles qu’il faut définir la directive worker_rlimit_nofile
. Cette dernière a justement pour but de configurer le ulimit. Il faut impérativement que le serveur démarre en root avant de passer à un utilisateur non privilégié, sans quoi il ne pourra augmenter cette limite. Par ailleurs, s’il y a d’autres logiciels qui utilisent le même utilisateur – nginx sous Debian – il est bon de leur en laisser quelques uns. Vous devrez dans ce cas avoir une valeur pour worker_connections
légèrement inférieur à worker_rlimit_nofile
.
Pour ma part, sauf cas particuliers, je trouve plus fiable de paramétrer le ulimit en utilisant la configuration du système et de se contenter d’attribuer la même valeur au ulimit et à worker_connections
(si Nginx est seul à tourner avec ce user).
En faisant worker_processes
x worker_connections
, on peut déjà se faire une idée du nombre de connexions simultanées que Nginx va pouvoir encaisser. Bien entendu, d’autres paramètres entrent en ligne de compte.
Par ailleurs, il ne s’agit pas non plus de s’obstiner à atteindre le plus grand nombre possible de connexions. Mieux vaut en effet servir 1000 connexions simultanées en 1s et passer aux 1000 suivantes, que d’en servir 2000 en 4s.
En dernier lieu, on permet aux workers d’accepter plusieurs nouvelles connexions de manière simultané en activant multi_accept
. Cela peut être d’une grande utilité lors de pics de trafic.
multi_accept on;
Les buffers permettent à Nginx de travailler en RAM plutôt que sur le disque. Je ne vais pas vous faire un dessin, vous savez bien que les accès disque sont infiniment plus lents que le travail en RAM. On va donc configurer les buffers pour que notre serveur puisse travailler en mémoire autant que faire se peut.
Il y a quatre types de buffers :
client_body_buffer_size
client_header_buffer_size
large_client_header_buffers
qui s'applique.client_max_body_size
large_client_header_buffers
Les réglages des buffers sont plus délicats et subjectifs que ceux des workers. Cela dépend énormément de votre application. client_body_buffer_size
définit la quantité maximale des données GET
ou POST
qui seront stockées en RAM. La question à se poser est donc la suivante : quelle proportion de vos requêtes sont supérieur à une taille donnée ? Ces requêtes nécessitent-elles d’être traitée très rapidement ? Par exemple, pour l’upload de gros fichiers, il est tout à fait tolérable d’écrire les fichiers sur le disque. En effet, le temps supplémentaire liée à l’écriture sur le disque est dérisoire par rapport au temps d’upload.
En revanche, il faut bien avoir à l’esprit que plus cette limite est élevée, plus votre serveur prête le flanc aux attaques DoS, puisqu’il allouera potentiellement plus de mémoire aux requêtes et arrivera donc potentiellement plus rapidement à saturation en RAM.
La logique prévalente pour les buffers est souvent d’essayer et d’aviser ! Dans une application permettant l’upload de fichiers, je laisserais client_body_buffer_size
à sa valeur par défaut de 16k
(sur processeurs 64 bits), idem pour client_header_buffer_size
qui est de 1k
.
client_max_body_size
aura ici une valeur élevée puisque je veux autoriser l’upload de fichiers (disons 20m
, là encore tout dépend du type de fichiers !). Enfin, je serais plus conservateur que les défauts (4 8k
) pour large_client_header_buffers
, et mettrais 2 3k
car mon applications possède des headers relativement modestes. En résumé :
client_body_buffer_size 16K;
client_header_buffer_size 1k;
client_max_body_size 20m;
large_client_header_buffers 2 3k;
J’insiste de nouveau sur ce point, tout dépend de votre application. Si votre applicatif soumet de gros articles en POST, il faudra peut-être revoir à la hausse les 16k
de client_body_buffer_size
. Il n’y a ici pas de règle générale.
Il existe des buffers spécifiquement dédiés aux proxies. Dans le cas où les buffers sont désactivés, Nginx commence l’envoie des données au client aussitôt qu’il les reçoit du backend serveur. Si le client est rapide, tout est pour le mieux. Cependant, si le client est moins véloce, ce fonctionnement oblige à conserver une connexion ouverte entre Nginx – le serveur de proxy – et le backend serveur ; ce qui peut s’avérer dommageable.
L’activation des buffers permet donc à Nginx de d’abord récupérer l’ensemble des données de la requêtes depuis le backend serveur, de libérer ce dernier, puis de servir les données au client.
proxy_buffering
proxy_buffer_size
8k
par défaut sur systèmes 64 bits. On peut ici laisser la valeur par défaut car les en-têtes dépassent rarement 8k
.proxy_buffers
8k
. Il s'agit de paramètres s'appliquant par requête. Ainsi, le réglage par défaut permettra de stocker dans les buffers des réponses jusqu'à 64kb
. À vous de voir si votre application retourne des résultats plus importants (sachant qu'ensuite les fichiers sont écrits sur le disque).proxy_buffering on;
proxy_buffer_size 1k;
proxy_buffers 12 4k;
Question existentielle s’il en est, les timeouts peuvent avoir un impact conséquent à la fois sur la vitesse ressentie par les utilisateurs, mais aussi sur la charge du serveur. Les timeouts se divisent également en plusieurs directives :
client_body_timeout
client_header_timeout
keepalive_timeout
send_timeout
keepalive_requests
Comme expliqué, étant donné le faible impact de performance sur Nginx, je préfère être généreux au départ et abaisser les timeouts au besoin :
client_body_timeout 30;
client_header_timeout 10;
keepalive_timeout 30;
send_timeout 60;
keepalive_requests 100;
Il ne fait aucun doute que la compression permet d’accélérer les transferts sur le réseau. Il est aussi un fait que compresser les ressources demande un peu de puissance processeur. Comme il existe plusieurs niveau de compression, il s’agit de trouver le juste milieu.
gzip on;
gzip_comp_level 5;
gzip_min_length 1000;
gzip_proxied any;
gzip_types
application/atom+xml
application/javascript
application/json
application/ld+json
application/manifest+json
application/rss+xml
application/vnd.geo+json
application/vnd.ms-fontobject
application/x-font-ttf
application/x-web-app-manifest+json
application/xhtml+xml
application/xml
font/opentype
image/bmp
image/svg+xml
image/x-icon
text/cache-manifest
text/css
text/plain
text/vcard
text/vnd.rim.location.xloc
text/vtt
text/x-component
text/x-cross-domain-policy;
Voici en général mes réglages. Le niveau de compression 5 est le juste milieu selon moi – on pourra toujours le baisser si on a trop de charge CPU. On ne compresse pas les fichiers de taille inférieure à 1ko (on n’y gagnera pas grand chose), on compresse sans distinction les éléments qui proviennent d’un proxy et on active la compression pour les fichiers textes, le js, les css, le json, le xml et d’autres un peu moins courants. Vous ne trouverez ici pas les images jpeg et png car ce sont déjà des fichiers compressés.
Sachez également que la directive gzip_buffers
peut s’avérer intéressante. Elle définie le nombre et la taille des buffers alloué à la compression. Par défaut, sur les architectures 64 bits, jusqu’à 16 buffers de 8k sont autorisés.
Tout va ici dépendre de la taille de vos fichiers. Il s’agit, comme expliqué dans ce post d’arbitrer entre le nombre de buffers (gérer de nombreux buffers consomme un peu de CPU) ou attribuer plus d’espace aux buffers (utilise plus d’espace mémoire). Si vous n’avez pas assez d’espace dans les buffers pour contenir l’ensemble de la réponse, Nginx attendra qu’une partie de la réponse soit envoyée au client et utilisera ensuite l’espace libéré.
Enfin, sachez aussi qu’il est possible, avec le module gzip_static, de pré-compresser des fichiers pour que Nginx puisse les servir sans avoir à les compresser à la volée.
Vous en avez peut-être entendu parler, Brotli est un algorithme de compression plus récent que Gzip qui offre de bien meilleurs taux de compression. Le module Brotli développé par Google n’est pas nativement intégré à Nginx et nécessite de le recompiler.
Nous ne pourrons donc pas compresser directement en Brotli. Cependant, nous pouvons profiter de la pré-compression en Brotli lors du processus de build et servir ces fichiers via quelques directives. C’est la stratégie que j’adopte avec Apache. On va donc créer un fichier de config pour Nginx /etc/nginx/conf.d/static_brotli.conf
.
set $extension "";
# si le navigateur accepte brotli
if ($http_accept_encoding ~ br) {
set $extension .br;
}
# et que le fichier .br existe sur le disque
if (-f $request_filename$extension) {
rewrite (.*) $1$extension break;
}
# on désactive gzip et en envoie en .br
location ~ /*.html.br {
gzip off;
types {}
default_type text/html;
add_header Content-Encoding br;
add_header Vary "Accept-Encoding";
}
location ~ /*.css.br$ {
gzip off;
types {}
default_type text/css;
add_header Content-Encoding br;
add_header Vary "Accept-Encoding";
}
location ~ /*.js.br$ {
gzip off;
types {}
default_type application/javascript;
add_header Content-Encoding br;
add_header Vary "Accept-Encoding";
}
location ~ /*.svg.br$ {
gzip off;
types {}
default_type image/svg+xml;
add_header Content-Encoding br;
add_header Vary "Accept-Encoding";
}
Il ne vous reste plus qu’à l’inclure depuis n’importe quelle directive server
ou location
. Par exemple, depuis un fichier de configuration d’un site :
server {
listen [::]:443;
listen 443;
server_name super-site.com;
root /var/www;
charset utf-8;
location / {
try_files $uri $uri.html $uri/ =404;
}
# tous vos fichiers seront servis en brotli pour les navigateurs compatibles
include conf.d/static_brotli.conf;
# brotli ne fonctionne qu'en TLS
include conf.d/ssl.conf;
ssl_certificate /etc/letsencrypt/live/super-site.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/super-site.com/privkey.pem;
ssl_trusted_certificate /etc/letsencrypt/live/super-site.com/chain.pem;
}
Grand classique du web, permettre aux navigateurs et proxies intermédiaires de placer certaines ressources en cache, c’est éviter au serveur d’avoir à les renvoyer plus tard. Selon la fréquence à laquelle changent vos fichiers, on peut attribuer une période de validité plus ou moins longue au cache. Disons deux mois pour l’exemple :
location ~* .(jpg|jpeg|png|gif|ico|css|js)$ {
expires 60d;
}
Nous cachons donc pour deux mois tous les fichiers d’extensions suivantes : jpg, jpeg, png, gif, ico, css, js.
Ce type de cache permet de conserver les métadonnées en mémoire, et donc de limiter l’I/O. Voici un exemple de configuration :
open_file_cache max=2000 inactive=5m;
open_file_cache_valid 2m;
open_file_cache_min_uses 2;
open_file_cache_errors on;
Cette configuration indique au serveur de mettre en cache 2000 open file descriptors et de les fermer si aucune requête les concernant n’est demandé au bout de 5 minutes. La validité des informations en cache est revérifiée après 2 minutes et un fichier doit être requêté un minimum de deux fois afin d’être valide pour le cache. Enfin, les fichiers d’erreurs sont ici également valables pour le cache.
Demander à NGINX d’écrire des logs, c’est encore lui demander une chose supplémentaire. Évidemment, le plus performant, c’est encore de ne pas logger ! On parle bien de l’access_log
, pas de l’error_log
qu’il n’est pas question de supprimer. D’autant plus que, théoriquement, il ne devrait pas y avoir trop d’erreurs donc la charge est faible concernant ce dernier.
Pour l’access_log
, si vous ne l’exploitez pas, le mieux est encore de le désactiver complètement avec access_log off;
. Cette directive prend sa place dans http
, server
ou location
. Vous pouvez donc définir un comportement global directement dans le nginx.conf
et/ou affiner seulement pour certains domaines ou répertoires.
Si vous ne souhaitez pas le désactiver totalement, d’autres options s’offrent à nous. Déjà, autant utiliser un buffer afin de réduire les I/O disque.
access_log /var/log/nginx/access.log combined buffer=10m flush=5m;
Dans l’exemple précédent, vous écrivez les logs sur le disque soit lorsque le buffer de 10MB est plein, soit toutes les 10mn (première condition remplie). Cela est bien entendu à moduler selon votre trafic. Pour un blog avec quelques centaines de visites pas jour, les 10MB de logs ne seront jamais atteints en 5 minutes. En revanche, pour un site à très fort trafic, à vous de voir s’il n’est pas judicieux d’augmenter la taille du buffer. Gardez cependant à l’esprit qu’en cas de crash, tout ce qui n’est pas écrit sur le disque est perdu.
Pour logger mais logger moins, il est également possible de logger de manière conditionnelle. On peut par exemple n’être intéressé que par les erreurs 4xx ou vouloir écrémer le gros des 2xx et 3xx qui indiquent juste que tout fonctionne bien.
# on ne log pas les 2xx & 3xx
map $status $loggable {
~^[23] 0;
default 1;
}
access_log /var/log/nginx/access.log combined buffer=10m flush=5m if=$loggable;
Petite précision au passage, la directive map
prend sa place dans un bloc http
. Si vous la placez dans un fichier de configuration vhost dans server ou location, vous aurez une belle erreur. On place dans notre directive map
au niveau http
et la directive access_log
dans location si elle ne doit pas s’appliquer de manière globale.
Par ailleurs, il arrive que l’on veuille définir une règle en fonction de plusieurs variables. Prenons un exemple concret. Nous avons une api à fort trafic et nous ne voulons donc pas tout logger par défaut. Nous nous contentons de logguer les erreurs avec la règle vue ci-dessus.
Néanmoins, certaines url sont particulièrement sensibles. Par exemple, nous avons un endpoint qui permet de créer un compte utilisateur. Nous ne voulons pas laisser un bot créer des comptes à la volée. Nous allons donc logger cette url en particulier et demander à fail2ban de bloquer les ips qui y accèdent de manière récurrente.
# on ne log pas les 2xx & 3xx
map $status $loggable_status {
~^[23] 0;
default 1;
}
# ici si l'url contient signup, on logge
map $request $loggable {
~signup 1;
default $loggable_status;
}
access_log /var/log/nginx/access.log combined buffer=10m flush=5m if=$loggable;
Bien que Nginx nomme toutes les variables SSL, vous savez très bien que le protocole est définitivement à bannir au profit de TLS. Petite parenthèse, si la sécurité vous intéresse, sachez qu’au delà du protocole, c’est aussi la suite de chiffrement qui assure la sécurité. Vous pouvez tester la sécurité de votre site avec ce scanner SSL/TLS et générer vos configurations serveurs avec le générateur de Mozilla.
Bref, revenons-en à nos moutons. Nous l’avons dit, le three-way handshake prend du temps et est couteux en ressources. Puisque le client et le serveur ce sont déjà entendus, il est possible de dire à Nginx de mettre en cache cette session. Ainsi, lorsqu’il voudra de nouveau établir une connexion, le client n’aura plus qu’à se rappeler aux bons souvenirs de Nginx. Le three-way handshake ne compte plus que deux allez-retours entre client et serveur et Nginx s’épargne quelques calculs cryptographiques. Bien que datant de 2011, ce post vous éclairera sur la théorie derrière cette histoire de cache.
Trois paramètres sont à considérer :
ssl_session_cache
builtin
ou shared
) ainsi que la taille de ce dernier. Il est à none
par défaut. La doc Nginx établit que 1MB permet de stocker 4000 sessions. À vous de juger combien de à sessions vous estimez devoir faire face et la durée de rétention que vous leur accordez. N'ayez crainte, au pire des cas Nginx invalidera les sessions prématurément, il n'y aura pas d'effusion de sang.ssl_session_timeout
ssl_buffer_size
Et bien entendu quelques valeurs d’exemples :
ssl_session_cache shared:SSL:10m;
ssl_session_timeout 24h;
ssl_buffer_size 1400;
En plus des caches, il y a une dernière optimisation qu’il est possible de réaliser, elle a pour doux nom l’OSCP stapling. Lorsqu’un serveur fourni un certificat, le client en vérifie la validité en interrogeant l’autorité émettrice du certificat. Évidemment, cela demande une requête supplémentaire au client. On peut éviter cela en demandant au serveur de joindre directement l’autorité émettrice et d’ainsi “agraffer” (d’où le stampling) la réponse signée et horodatée de l’autorité afin de prouver la validité du certificat.
ssl_stapling on;
ssl_stapling_verify on;
resolver 8.8.8.8 8.8.4.4 216.146.35.35 216.146.36.36 valid=60s;
resolver_timeout 2s;
Il est possible de préciser un resolver, en cas d’absence de la directive resolver
, le serveur DNS par défaut sera interrogé. C’est d’ailleurs souvent avantageux car dans le cas d’un serveur, il se trouve en général dans le même datacenter.
On arrive là dans le domaine de l’optimisation de pointe ! Il est possible de spécifier à Nginx la manière dont quelques options de TCP doivent être gérées. De manière synthétique, ces réglages vont nous permettre de :
sendfile
: d’optimiser la copie des données depuis le file descriptors vers le buffer du kernel (ok, c’est compliqué !),tcp_nopush
: n’envoyer les données que lorsque le paquet IP est rempli (quantité de données égale au MSS),tcp_nodelay
: diminuer la latence de 200ms avant l’envoie des données sur le réseau (on attend ici pas que le paquet soit rempli. Utilisés conjointenenement avec tcp_nopush
, NGINX n’active l’option tcp_nodelay
que lorsqu’on atteint la fin des données à envoyer et qu’il n’est pas possible de remplir un paquet).Pour comprendre tout cela dans les détails, vous pouvez vous référer à ce très bon article qui plonge littéralement dans les spécificité du noyau.
sendfile on;
tcp_nopush on;
tcp_nodelay on;
Dans le cas de très nombreuses connexions entre le proxy et le serveur backend, on peut faire face à un problème. Le TCP Maximum Segment Lifetime définit la durée de vie maximale d’une connexion TCP et impacte directement le TIME_WAIT
– temps à attendre avant de fermer une connexion TCP, égale à 2*MSL.
Là où le bas peut blesser, c’est qu’un grand nombre de connexions en TIME_WAIT
peut empêcher d’en ouvrir de nouvelles si on arrive à épuisement du nombre de connexions ouvertes entre deux ip – défini par le couple port/ip, soit le nombre de ports définis dans /proc/sys/net/ipv4/ip_local_port_range
, en général ± 30 000. Dans cette configuration, on ne pourra effectuer plus de 30000/(2 x MSL) requêtes par secondes, soit 30000/(2 x 30), soit 500 req/sec.
Plusieurs solutions sont possibles :
net.ipv4.tcp_tw_reuse
afin de recycler plus rapidement les connexions en TIME_WAIT
,tcp_fin_timeout
.Le plus efficace et le moins risqué selon moi est d’utiliser keepalive. Cependant, tous les serveurs ne le prennent pas en charge (Haproxy par exemple). Il vous reste donc au choix de diminuer le tcp_fin_timeout
ou d’utiliser net.ipv4.tcp_tw_reuse
(ou les deux). Je vous invite à lire cet article de qualité, lequel traite du problème de connexions en TIME_WAIT
et des remèdes potentiels (dont tcp_fin_timeout
).
Vous pourrez modifier ces paramètres respectivement dans /proc/sys/net/ipv4/tcp_fin_timeout
et /proc/sys/net/ipv4/tcp_tw_reuse
.
En dernier lieu, nous allons nous pencher sur quelques optimisations qui s’effectuent au niveau du block server
, elles se placeront donc en général dans un VHOST.
Nous l’avons déjà vu plus haut, une connexion TCP s’effectue en trois temps, cela porte le doux nom de Three-way handshake. En gros, cela demande trois échanges de trames TCP/IP entre le client et le serveur. Le premier envoie un paquet SYN (demande de connexion [synchronize]), le second répond SYN/ACK (en gros : 5/5, Roger! [synchronize acknowledge]) et enfin, le client confirme que tout est ok avec un ACK.
À partir de là, la connexion est ouverte, et théoriquement, un socket est créé et le processus en écoute sur ce socket est réveillé : attribution de ressources Nginx dans le cas présent. Pour autant, tant qu’aucune vraie requête n’est effectuée, Nginx n’a rien à faire. L’option TCP_DEFER_ACCEPT
du kernel permet donc de ne réveiller le processus que lors de l’envoie effectif de données. Cette option se traduit dans nginx par deferred
.
Dans un autre registre, ce n’est pas tout à fait lié à TCP, mais j’en parle ici quand même, activer le HTTP/2 peut avoir un impact significatif sur les performances. Pour plus de détails sur le protocole, je vous laisse jeter un œil sur l’article Wikipedia.
Les deux optimisations dont nous venons de parler se définissent dans le bloc server
et au niveau de la directive listen
.
server {
# on active deferred
# pour ipv6 sur le port 80 (http)
listen [::]:80 default_server deferred;
# pour ipv4 sur le port 80 (http)
listen 80 default_server deferred;
# pour ipv6 sur le port 443 (https)
listen [::]:443 ssl http2 deferred;
# pour ipv4 sur le port 443 (https)
listen 443 ssl http2 deferred;
return 444;
}
Quelques précisions supplémentaires sur ce morceau de config. Vous noterez que l’on active HTTP/2 seulement pour le TLS. Il est possible de l’activer en HTTP non sécurisé, mais les navigateurs ne le supportent pas.
Vous remarquez aussi peut-être le default_server
. Cela indique à Nginx d’utiliser ce VHOST si l’en-tête Host
n’est pas passée avec le requête. Dans le cas d’un accès direct via l’ip par exemple. Et en dernier lieu, return 444
signifie que dans ces cas là, la connexion sera réinitialisée puisque si plusieurs sites sont hébergés sur cette même ip:port, en l’absence de Host
, il n’est pas possible de savoir lequel on doit servir.
Il y a de nombreuses optimisations potentielles au niveau de la pile TCP/IP, non spécifiques à Nginx, nombre d’entre elles sont détaillées dans cette très bonne ressource dont j’encourage la lecture.
Il arrive que certaines opérations bloquantes soient lentes (comme la lecture de fichiers sur le disque). Les requêtes de fichiers de taille importante qui ne tiennent pas en mémoire par exemple, bloqueront un thread de Nginx jusqu’à ce que le disque retourne le fichier en question. Admettons-le, c’est assez dommage. Heureusement, Nginx a une solution pour ça !
Il s’agit de placer cette requête dans une “file d’attente” et de traiter d’autres requêtes en attendant que le disque ait fini son opération de lecture. Ainsi, on ne bloque pas plusieurs requêtes en attendant une I/O pour l’une d’entre elle, on place cette dernière en attente et on utilise le thread ainsi libéré pour servir d’autres requêtes.
Un article sur le blog de Nginx détaille le fonctionnement de la thread pool et présente un benchmark où les performances en charge sont multipliées par 9 ! Il faut pour cela utiliser l’option aio
:
location / {
root /var/www;
aio threads;
directio 5m;
}
Ici, vous remarquez aussi l’option directio
. Cette dernière s’utilise toujours conjointement à aio
. Lorsque vous activez aussi l’option sendfile
vue plus haut, aio
est utilisé pour les fichiers de taille égale ou supérieure à directio
, tout le reste utilise sendfile
.
N’hésitez pas à me faire part de vos remarques et compléments d’infos en commentaires. Avez-vous été confronté à des problèmes ? Avez-vous pu augmenter les performances ? Faites-nous part de vos expériences !