Je configure NGINX Worker de manière à ce que les paramètres `worker_processes`, `worker_connections` et `worker_rlimit_nofile` soient parfaitement alignés et qu'Epoll fonctionne dans la boucle d'événements. Je peux ainsi utiliser Noyaux du CPU Assure une efficacité optimale, permette de prévoir l'évolutivité des connexions simultanées et maintient les latences à un faible niveau lors des pics de charge.
Points centraux
Les points clés suivants te fournissent des indications immédiates pour mettre en place une configuration robuste des workers NGINX.
- worker_processes le lier au nombre de cœurs logiques, de préférence avec „ auto “.
- worker_connections les régler de manière à couvrir largement les pics réels.
- rlimit_nofile et augmenter les limites du système d'exploitation en fonction du débit de connexion.
- epoll et activer multi_accept afin d'exploiter efficacement la boucle d'événements.
- Tests de charge avancer et affiner progressivement, par petites étapes.
Architecture NGINX : comprendre les nœuds maîtres et les nœuds de travail
Je sépare les tâches de Master et les workers : le maître charge les configurations, ouvre les sockets et lance les processus, tandis que les workers traitent les requêtes dans la boucle d'événements. Chaque worker fonctionne de manière autonome, réagit aux événements et peut gérer des milliers de connexions sans provoquer de blocages. Ce modèle est particulièrement efficace lorsque j’utilise les cœurs de processeur de manière optimale et que j’exploite au mieux la boucle d’événements via epoll. Je tiens compte du fait que chaque saut de proxy supplémentaire mobilise des ressources de connexion, ce qui se répercute sur les limites. Comprendre ces rôles permet de prendre des décisions éclairées concernant Ressources et permet d'éviter les goulots d'étranglement à un stade précoce.
Bien articuler les trois directives clés
Je considère worker_processes, worker_connections et worker_rlimit_nofile ne doivent jamais être réglés isolément, mais toujours ensemble. Le nombre total de connexions possibles correspond au nombre de workers multiplié par le nombre de connexions par worker ; c'est à partir de là que je détermine les limites pour les descripteurs de fichiers. Si ces paramètres ne sont pas harmonisés, je me heurte à des erreurs du type „ too many open files “ ou à des délais d’attente trop longs. En cas de charge élevée, j’ai besoin d’une chaîne cohérente : un nombre suffisant de processus, un nombre généreux de connexions, une valeur rlimit_nofile correctement augmentée et des paramètres du système d’exploitation adaptés. C’est ainsi que j’évite qu’un Limite a réduit la capacité totale.
worker_processes : choisir le nombre de manière ciblée
Je mets worker_processes En règle générale, on le règle sur „ auto “ afin que NGINX détecte le nombre de cœurs logiques du processeur et puisse utiliser chaque cœur. Un worker par cœur évite les changements de contexte inutiles et répartit la charge de manière optimale, ce qui permet de prévoir le temps de réponse. Sur les machines dotées d’un très grand nombre de cœurs, je teste délibérément des nombres de workers plus faibles afin de comparer les coups de cache et l’utilisation des cœurs. Si les métriques indiquent que les cœurs sont surchargés ou que les échecs TLB augmentent, j’ajuste progressivement le nombre de workers. Mesurer d’abord, modifier ensuite : c’est ainsi que je m’assure d’obtenir des résultats fiables. Résultats.
worker_connections : augmenter les connexions de manière planifiée
Je choisis la worker_connections en fonction du trafic cible et de la composition des protocoles, souvent à partir de 2048 ou 4096. Pour les API très sollicitées, j'envisage 8192, à condition que les limites du système d'exploitation et la mémoire vive le permettent. Je vérifie chaque augmentation à l’aide de tests de charge, car les connexions ouvertes mobilisent de la mémoire et influencent le comportement en amont. Lorsque les handshakes SSL ou les transferts en upload volumineux prédominent, je m’appuie davantage sur les profils CPU et E/S, et pas uniquement sur les chiffres bruts de connexions. Je m’assure ainsi que le nombre défini par worker Capacité reste également utilisable dans la pratique.
Synchroniser worker_rlimit_nofile et les limites du système d'exploitation
Je veille à ce que rlimit_nofile couvre au moins la capacité totale théorique et est souvent configuré avec une marge de sécurité. Pour les scénarios de proxy inverse, je prévois un deuxième descripteur vers l'amont pour chaque connexion client. En conséquence, je règle volontiers rlimit_nofile à une valeur deux fois supérieure au nombre de connexions simultanées attendues. J'augmente les limites du noyau et de l'utilisateur (ulimit -n, fs.file-max) de manière à ce que NGINX puisse réellement exploiter ces valeurs. Si des messages concernant des fichiers ouverts apparaissent dans le journal des erreurs, j'augmente rapidement ces limites et surveille la Latence à nouveau sous charge.
Bloc « Événements » : utiliser efficacement epoll et multi_accept
J'active dans le bloc « Événements » epoll et je règle multi_accept sur „ on “ afin que les workers acceptent les connexions en attente en une seule fois. Epoll réduit la surcharge en cas de nombreux sockets simultanés et s'accorde parfaitement avec la conception non bloquante de NGINX. Ces options s’avèrent particulièrement utiles lors des pics de trafic, car elles accélèrent la phase d’acceptation et permettent de passer plus rapidement au traitement proprement dit. Sous Linux, c’est ma configuration par défaut, que je ne modifie que dans de rares cas particuliers. Si vous souhaitez approfondir le sujet, comparez le modèle de boucle d’événements avec Pool de threads vs. boucle d'événements et en tire la conclusion suivante : conclusions pour son propre environnement.
Affinité CPU : attribuer les workers à des cœurs
Je mets affinité_cpu_des_tâches de manière ciblée lorsque les charges de travail sont constantes et dépendantes du processeur. Je répartis le schéma d'affectation à l'aide de masques de bits afin d'éviter les changements de contexte et de favoriser la localité du cache. Avec quatre cœurs, j’attribue les masques de manière à ce que chaque worker dispose de son propre cœur. Je vérifie ensuite les taux de manques de cache, les latences médianes et les 99e centiles afin d’observer clairement l’effet. Vous trouverez des explications plus détaillées sur l’affinité et le NUMA sous forme concise sur L'affinité CPU en pratique, ce qui, lors du réglage fin de Travailleur- les mises en page.
Planification de la capacité : marge de sécurité et tests de charge
Pour les liaisons, je prévois un Tampon qui soit nettement supérieur aux pics observés, afin que les pics de trafic de courte durée ne dépassent pas directement les limites. Si je double la charge de pointe comme point de départ, je dispose d'une marge de manœuvre solide dans de nombreux scénarios. En cas de trafic très fluctuant, j’étends encore la marge de sécurité jusqu’à ce que les 99e centiles s’affichent correctement. Ensuite, je vérifie les goulots d’étranglement à l’aide d’outils tels que wrk ou k6, je surveille les taux d’erreur et j’examine les connexions ouvertes dans l’état. Ce n’est que lorsque les métriques sont cohérentes que j’augmente ou que je réduis de manière ciblée certaines Valeurs.
Configuration et exemples de calcul
Je calcule la capacité de connexion en multipliant le nombre de workers par le nombre de connexions par worker, puis je fixe des limites supérieures en fonction de ce résultat. Avec quatre cœurs de processeur, le paramètre « auto » et 4 096 connexions par worker, j'obtiens théoriquement 16 384 connexions simultanées. Dans les scénarios de proxy, je règle plutôt rlimit_nofile à 32 768 ou plus, afin d’inclure les sockets en amont. Pour les petites machines à deux cœurs, 2 048 connexions par worker suffisent souvent, à condition que la part des transferts en upload et des connexions TLS reste modérée. Le tableau suivant aide à classer les valeurs initiales:
| Noyaux du CPU | worker_processes | worker_connections (Début) | Valeur minimale de rlimit_nofile (valeur indicative) | Remarque |
|---|---|---|---|---|
| 2 | auto (≈2) | 2048 | ≥ 4 096 | Réserve Prévoir pour TLS/proxy |
| 4 | auto (≈4) | 4096 | ≥ 16 384 | Avec un proxy, on observe souvent un facteur 2 pour les FD |
| 8 | auto (≈8) | 4096-8192 | ≥ 32 768 | Test de charge décide d'une augmentation |
| 16+ | voiture, voire moins | 8192+ | ≥ 65 535 | Tester avec sensibilité et réserve |
Workers NGINX et sources en amont : bien pondérer les scénarios
Je fais la distinction entre la diffusion statique, le fonctionnement en proxy inverse et la charge de la passerelle API, car ils constituent les Travailleur- La configuration peut varier en fonction des besoins. Les contenus statiques mobilisent moins de ressources, tandis que le protocole TLS, la compression et les connexions en amont sollicitent davantage le processeur et les descripteurs de fichiers (FD). Plus les clés SSL sont volumineuses et plus il y a de „ handshakes “, plus le paramètre « un worker par cœur » s’avère avantageux. Les transferts volumineux font pencher la balance vers les E/S, ce qui m’amène à me concentrer davantage sur rlimit_nofile et les tampons réseau. En cas de temps d’attente perceptibles lors de l’acceptation ou des réponses du backend, la vue d’ensemble m’aide à Files d'attente et latence, afin d'éviter les goulots d'étranglement ciblé à résoudre.
Flux de travail pratique : étape par étape vers un serveur plus rapide
Je commence par dresser un état des lieux de tous les éléments pertinents Valeurs Dans le fichier nginx.conf, je vérifie les cœurs de processeur, les valeurs ulimit et les paramètres du noyau. Ensuite, je règle worker_processes sur « auto », je définis worker_connections sur 4096, par exemple, et j'augmente généreusement la valeur de rlimit_nofile. Dans le bloc Events, j'active epoll ainsi que multi_accept, puis je vérifie les journaux à l'aide d'un rechargement. Je procède ensuite à des tests de charge dans des conditions reproductibles, au cours desquels j'observe les temps de réponse, les taux d'erreur et les connexions ouvertes. Lors de l'ajustement fin, je ne modifie toujours qu'une seule variable, je documente chaque étape et j'évalue les effets dans les Métriques.
Environnement d'hébergement : ressources, noyau, réseau
Je veille à ce qu'il y en ait suffisamment CPU- des cœurs, suffisamment de mémoire vive, des SSD ou NVMe rapides et un noyau Linux récent. C'est la seule façon de garantir le fonctionnement fiable d'epoll, des piles TCP modernes et des fonctionnalités d'offload pertinentes. J'adapte les paramètres réseau tels que `somaxconn` et `tcp_max_syn_backlog` au nombre cible de connexions afin de limiter la longueur des files d'attente d'acceptation. Un fournisseur offrant des performances d’E/S robustes et une configuration système librement accessible s’avère clairement avantageux dans ce contexte. Des comparaisons montrent que les services avec des Ressources Augmenter considérablement les marges de manœuvre de NGINX.
Stratégie de maintien de connexion : connexions client et en amont
J'utilise délibérément Keepalive comme levier pour optimiser la capacité et réduire la latence. Côté client, je configure keepalive_timeout pas trop élevé, afin que les sockets inactives ne soient pas inutilement worker_connections bloquer. Des valeurs comprises entre 10 et 30 s constituent souvent pour moi un bon compromis entre réutilisation et mobilisation des ressources. Avec keepalive_requests Je limite le nombre de requêtes par connexion afin de couper les sessions de longue durée et d'éviter la pression sur la mémoire. Côté amont (proxy inverse), je maintiens des connexions persistantes avec keepalive dans le bloc « upstream », ce qui permet d'éviter les handshakes et la configuration TCP. Pour cela, je dimensionne le nombre par backend de manière prudente en fonction de la capacité du backend (max_conns), sinon je gère moi-même les files d'attente côté amont. Important : chaque socket Keepalive compte comme une connexion ouverte et nécessite des descripteurs de fichier (FD) ; j'en tiens compte dans rlimit_nofile et ma planification de la marge dynamique.
Optimisation des listes : reuseport, backlog et stratégie d'acceptation
Je répartis la charge de réception de manière uniforme en SO_REUSEPORT activer (listen … reuseport). Chaque worker dispose ainsi de sa propre file d'attente d'acceptation, ce qui réduit les „ thundering herds “ et évite les goulots d'étranglement. En combinaison avec multi_accept j'accélère sensiblement la phase d'acceptation. Les listes-backlog (listen … backlog=) et les paramètres correspondants du noyau (somaxconn, tcp_max_syn_backlog), je les définis de manière généreuse afin que les pics de trafic ne soient pas perdus au niveau de l'entrée du socket. L'option reporté reporte la réponse « Accept » jusqu'à ce que les données soient disponibles – cela peut s'avérer utile en cas de nombreuses requêtes éphémères ; sinon, je compare les résultats lors des tests. Je me demande si je accept_mutex Je détermine ce dont j'ai besoin à l'aide d'un benchmark : avec reuseport, il est généralement superflu ; sans reuseport, il peut améliorer l'équité, mais nécessite une certaine coordination. Je prends ici une décision fondée sur les données, jamais sur une intuition.
Configurer les délais d'expiration et les files d'attente de manière stable
Je mets Timeouts de manière à ce que les clients lents n'engorgent pas les workers : client_header_timeout et client_body_timeout Je le garde suffisamment concis pour éviter les blocages, mais suffisamment détaillé pour les utilisateurs réels. send_timeout empêche que les réponses ne soient bloquées vers le client. Dans le contexte du proxy, je définis proxy_connect_timeout, proxy_read_timeout et proxy_send_timeout rigoureux, afin que les backends bloqués ne paralysent pas le frontend. Pour les backends dont le parallélisme est limité, j'utilise file d'attente dans le bloc « upstream » avec un délai d'expiration, afin d'amortir les pics de trafic et de signaler les erreurs 503 de manière contrôlée, au lieu de lier tous les workers aux sockets en amont en attente. De plus, je stabilise le système avec limit_req (Burst/Delay) et limit_conn des chemins sensibles, afin d'éviter que certains clients ou robots ne mobilisent de manière disproportionnée les ressources.
Mise en mémoire tampon, sendfile et AIO : choisir judicieusement les méthodes d'E/S
Je mets sendfile pour les fichiers statiques et combine-le avec tcp_nopush/tcp_nodelay en fonction de la charge de travail, afin de regrouper efficacement les paquets ou de réduire les latences interactives. Pour les fichiers volumineux, j'utilise directio à partir d'un certain seuil, afin d'éviter la pollution du cache et que le cache de page ne soit pas évincé. En mode proxy, c'est moi qui décide si proxy_buffering est utile (transfert rapide vers le client, lecture en amont découplée) ou si, en cas de charges de streaming, je préfère proxy_request_buffering réduire, afin de lancer les téléchargements dès que possible. Les tailles des proxy_buffers, proxy_buffer_size et tampons d'en-tête de client volumineux Je le gère de manière ciblée afin d'éviter que la consommation de mémoire par connexion n'explose. Pour des accès aux fichiers qui sollicitent moins le processeur, j'envisage aio (natif ou threads), mais effectuez des tests approfondis, car les caractéristiques de la boucle d'événements et celles des E/S s'influencent mutuellement.
HTTP/2, HTTP/3 et TLS : impact sur la capacité des workers
Je tiens compte du fait que HTTP/2 et HTTP/3 Modifier la dynamique des connexions : de nombreuses requêtes s'exécutent en tant que flux via un nombre réduit de connexions TCP ou QUIC. Cela réduit le nombre de connexions, mais augmente la charge sur le processeur et les besoins en mémoire par connexion (multiplexage, compression des en-têtes, TLS/QUIC). Mon worker_connections Je ne l'interprète donc pas aveuglément comme „ un nombre égal de requêtes “. Je constate flux simultanés par connexion et par paire keepalive_timeout et, le cas échéant,. http2_max_concurrent_streams . Du côté TLS, je tire parti de la reprise de session (tickets/cache) et de l'OCSP stapling ; cela me permet d'éviter des handshakes coûteux et de maintenir des latences faibles. L'inconvénient : des keepalives plus longs mobilisent des FD et de la RAM – je prévois donc rlimit_nofile et des quotas de mémoire assortis de réserves réalistes. Pour les algorithmes de chiffrement gourmands en ressources CPU, il vaut la peine de tester l'affinité et les accélérateurs cryptographiques modernes.
Observabilité : état, journaux et métriques
J'assure la transparence grâce à une approche allégée Statut-Endpoint (par exemple, stub_status) pour afficher les connexions actives, les états « Reading », « Writing » et « Waiting », ainsi que les requêtes acceptées. Dans les journaux, je limite le bruit : un format compact log_format Les informations relatives à l'heure, au statut, aux temps en amont et au nombre d'octets suffisent pour la plupart des analyses. En cas de QPS très élevé, je désactive le journal d'accès de manière sélective (en fonction de l'emplacement) ou je mets les journaux en mémoire tampon de manière asynchrone afin que les E/S ne ralentissent pas le système. Je configure le journal d'erreurs sur warn ou erreur et ne bascule que brièvement vers debug. Je corrèle en permanence les latences (médiane/95e/99e centile), les connexions ouvertes, les taux d'erreur du backend et la charge CPU par worker ; cela me permet d'ajuster les trois directives clés et de détecter à un stade précoce les effets de saturation.
Conteneurs et environnements virtuels : transmettre les limites de manière claire
Je vérifie dans les conteneurs les cgroup- Définissez les limites pour le processeur, la mémoire vive et les PID, puis harmonisez-les avec les paramètres NGINX. ulimit -n doit être suffisamment élevé au sein du conteneur, sinon mes ajustements de `rlimit_nofile` ne serviront à rien. Pour les quotas CPU (par exemple, 2 vCPU), je définis worker_processes En conséquence, afin que la planification ne provoque pas de congestion artificielle. À proximité du réseau, je bénéficie, dans les modes réseau „ host “, d’une latence de surcharge réduite, tandis que les superpositions impliquent des sauts supplémentaires. Sur les hôtes multi-NUMA, je veille à l’affinité et aux sockets de mémoire afin que les workers ne fonctionnent pas sur plusieurs nœuds. Il en va de même pour l’affinité IRQ et RPS/XPS : si les chemins, de la carte réseau à l’IRQ jusqu’au cœur du worker, sont cohérents, les pics de latence diminuent de manière mesurable.
Cycle de vie d'une connexion : ports éphémères, TIME_WAIT et réserves
Je prévois suffisamment ports éphémères (ip_local_port_range) lorsque NGINX agit en tant que client actif vis-à-vis des serveurs en amont. En cas de débit de connexion très élevé, j’évite une fluctuation excessive des ports grâce à la fonctionnalité „ Upstream Keepalive “, ce qui réduit les piles TIME_WAIT. Je n’utilise les options du noyau relatives à la « réutilisation » qu’avec prudence ; les piles modernes optimisent déjà beaucoup de choses en interne. Il est plus stable de contrôler la durée de vie des connexions à l’aide de valeurs de keepalive et de délai d’expiration raisonnables et reuseport pour assurer une répartition équitable. Dans le calcul des capacités, je tiens toujours compte, outre les clients, de l'amont : souvent, ce sont les FD qui constituent le véritable facteur limitant, et non la « porte d'entrée ».
Rechargements et déploiements fluides, sans interruption
J'utilise le modèle maître/esclave pour rechargements en douceur: Le maître charge de nouvelles configurations ; les anciens workers s'arrêtent tandis que les nouveaux prennent le relais en toute transparence. Avec worker_shutdown_timeout je laisse aux requêtes le temps de se terminer correctement, sans bloquer les ressources. J'associe les déploiements sans interruption sur l'upstream à des contrôles d'intégrité et proxy_next_upstream-Des règles pour éviter que certains backends défaillants ne fassent grimper la latence globale. Lorsque je modifie la configuration, je ne change toujours qu’un seul paramètre et je vérifie les effets dans les journaux et les métriques ; cela me permet d’éviter les erreurs dues à la confusion et de garantir la reproductibilité des performances.
Résumé succinct
Je me connecte worker_processes en fonction du nombre de cœurs (idéalement « auto »), je règle `worker_connections` en fonction de la charge de pointe et j'augmente généreusement `rlimit_nofile` ainsi que les limites du système d'exploitation. Dans le bloc « Events », j'utilise `epoll` et `multi_accept`, je vérifie le tout à l'aide de tests de charge reproductibles, puis j'ajuste les paramètres par petites étapes. Pour les charges de travail proxy, je prévois des descripteurs supplémentaires et je teste l’affinité CPU lorsque les charges de travail sont constantes. Une pile correctement configurée avec un noyau adapté, des E/S rapides et des paramètres réseau pertinents fait toute la différence. C’est ainsi que j’obtiens NGINX fiables dans le domaine des performances, ce dont les sites et les API exigeants ont besoin.


