Maison >interface Web >js tutoriel >Utiliser Node.js avec Nginx pour implémenter network_node.js à charge élevée
Lorsqu'il s'agit de créer des applications Web à haut débit, NginX et Node.js se marient naturellement. Ils sont tous conçus sur la base du modèle événementiel et peuvent facilement surmonter le goulot d'étranglement C10K des serveurs Web traditionnels tels qu'Apache. La configuration par défaut peut déjà atteindre une simultanéité élevée, mais si vous souhaitez atteindre plus de milliers de requêtes par seconde sur du matériel bon marché, il reste encore du travail à faire.
Cet article suppose que les lecteurs utilisent le HttpProxyModule de NginX pour agir comme proxy inverse pour le serveur node.js en amont. Nous présenterons le réglage de sysctl dans les systèmes Ubuntu 10.04 et supérieurs, ainsi que le réglage des applications node.js et NginX. Bien sûr, si vous utilisez un système Debian, vous pouvez également atteindre le même objectif, mais les méthodes de réglage sont différentes.
Réglage du réseau
Si vous ne comprenez pas d'abord les mécanismes de transmission sous-jacents de Nginx et Node.js et n'effectuez pas une optimisation ciblée, aussi détaillée que soit l'optimisation des deux, cela peut être en vain. Normalement, Nginx connecte le client et les applications en amont via des sockets TCP.
Notre système dispose de nombreux seuils et restrictions pour TCP, qui sont définis via les paramètres du noyau. Les valeurs par défaut de ces paramètres sont souvent définies à des fins générales et ne peuvent pas répondre aux exigences de trafic élevé et de courte durée de vie des serveurs Web.
Voici quelques paramètres candidats au réglage de TCP. Pour les rendre efficaces, vous pouvez les placer dans le fichier /etc/sysctl.conf ou les placer dans un nouveau fichier de configuration, tel que /etc/sysctl.d/99-tuning.conf, puis exécuter sysctl -p pour laissez le noyau les charger. Nous utilisons sysctl-cookbook pour effectuer ce travail physique.
Il convient de noter que les valeurs répertoriées ici sont sûres à utiliser, mais il est tout de même recommandé d'étudier la signification de chaque paramètre afin de choisir une valeur plus appropriée en fonction de votre charge, de votre matériel et de votre utilisation.
Soulignez-en quelques-uns importants.
Afin de servir le client en aval pour l'application en amont, NginX doit ouvrir deux connexions TCP, une vers le client et une vers l'application. Lorsqu'un serveur reçoit de nombreuses connexions, les ports disponibles du système seront rapidement épuisés. En modifiant le paramètre net.ipv4.ip_local_port_range, vous pouvez augmenter la plage de ports disponibles. Si une telle erreur est trouvée dans /var/log/syslog : "possible SYN Flooding sur le port 80. Envoi de cookies", cela signifie que le système ne trouve pas de port disponible. L'augmentation du paramètre net.ipv4.ip_local_port_range peut réduire cette erreur.
Lorsque le serveur doit basculer entre un grand nombre de connexions TCP, un grand nombre de connexions à l'état TIME_WAIT seront générées. TIME_WAIT signifie que la connexion elle-même est fermée, mais que les ressources n'ont pas été libérées. Définir net_ipv4_tcp_tw_reuse sur 1 permet au noyau d'essayer de recycler les connexions lorsque cela est sûr, ce qui est beaucoup moins cher que de rétablir de nouvelles connexions.
Il s'agit du temps minimum qu'une connexion dans l'état TIME_WAIT doit attendre avant de recycler. Le rendre plus petit peut accélérer le recyclage.
Comment vérifier l'état de la connexion
Utilisez netstat :
ou utilisez ss :
NginX
À mesure que la charge sur le serveur Web augmente progressivement, nous commencerons à rencontrer d'étranges limitations de NginX. La connexion est interrompue et le noyau continue de signaler une inondation SYN. À l'heure actuelle, la charge moyenne et l'utilisation du processeur sont très faibles, et le serveur peut évidemment gérer plus de connexions, ce qui est vraiment frustrant.
Après enquête, il a été constaté qu'il existe de nombreuses connexions dans l'état TIME_WAIT. Voici le résultat de l'un des serveurs :
Transport IP total IPv6
*541 - - -
BRUT 0 0 0 0
UDP 13 10 3
TCP 326 325 1
INET 339 335 4
FRAG 0 0 0 0
Il y a 47 135 connexions TIME_WAIT ! De plus, on peut voir d'après ss qu'il s'agit toutes de connexions fermées. Cela indique que le serveur a consommé la plupart des ports disponibles et implique également que le serveur alloue de nouveaux ports pour chaque connexion. Le réglage du réseau a quelque peu aidé à résoudre le problème, mais il n'y avait toujours pas assez de ports.
Après des recherches plus approfondies, j'ai trouvé un document sur la commande uplink keepalive, qui se lit comme suit :
Intéressant. En théorie, cette configuration minimise le gaspillage de connexions en transmettant les requêtes via les connexions mises en cache. La documentation mentionne également que nous devons définir proxy_http_version sur "1.1" et effacer l'en-tête "Connexion". Après des recherches plus approfondies, j'ai trouvé que c'était une bonne idée, car HTTP/1.1 optimise considérablement l'utilisation des connexions TCP par rapport à HTTP1.0 et Nginx utilise HTTP/1.0 par défaut.
Après modification comme suggéré dans le document, notre fichier de configuration de liaison montante devient comme ceci :
J'ai également modifié les paramètres du proxy dans la section serveur comme suggéré. Dans le même temps, un proxy_next_upstream a été ajouté pour ignorer les serveurs défaillants, le keepalive_timeout du client a été ajusté et le journal d'accès a été désactivé. La configuration devient comme ceci :
client_max_body_size 16M;
keepalive_timeout 10;
emplacement / {
proxy_next_upstream délai d'attente d'erreur http_500 http_502 http_503 http_504;
proxy_set_header Connexion "";
proxy_http_version 1.1;
proxy_pass http://backend_nodejs;
>
access_log off;
error_log /dev/null crit;
>
Après avoir adopté la nouvelle configuration, j'ai constaté que les sockets occupés par les serveurs étaient réduits de 90%. Les requêtes peuvent désormais être transmises en utilisant beaucoup moins de connexions. La nouvelle sortie est la suivante :
Total : 558 (noyau 604)
TCP : 4675 (estab 485, fermé 4183, orphelin 0, synrecv 0, timewait 4183/0), ports 2768
Transport IP total IPv6
*604 - - -
BRUT 0 0 0 0
UDP 13 10 3
TCP4924911
INET 505 501 4
Node.js
Grâce à la conception basée sur les événements qui gère les E/S de manière asynchrone, Node.js peut gérer un grand nombre de connexions et de requêtes dès le départ. Bien qu'il existe d'autres méthodes de réglage, cet article se concentrera principalement sur l'aspect processus de node.js.
Le nœud est monothread et n'utilise pas automatiquement plusieurs cœurs. En d’autres termes, l’application ne peut pas obtenir automatiquement toutes les capacités du serveur.
Réaliser le clustering des processus Node
Nous pouvons modifier l'application pour qu'elle crée plusieurs threads et reçoive des données sur le même port, permettant ainsi à la charge de s'étendre sur plusieurs cœurs. Node dispose d'un module cluster qui fournit tous les outils nécessaires pour atteindre cet objectif, mais les ajouter à l'application nécessite beaucoup de travail manuel. Si vous utilisez express, eBay dispose d'un module appelé cluster2 qui peut être utilisé.
Empêcher le changement de contexte
Lors de l'exécution de plusieurs processus, vous devez vous assurer que chaque cœur de processeur n'est occupé que par un seul processus à la fois. De manière générale, si le CPU possède N cœurs, nous devons générer N-1 processus d’application. Cela garantit que chaque processus bénéficie d'une tranche de temps raisonnable, laissant un cœur libre pour que le planificateur du noyau puisse exécuter d'autres tâches. Nous devons également nous assurer qu'aucune autre tâche autre que Node.js n'est exécutée sur le serveur pour éviter les conflits de CPU.
Nous avons commis une erreur une fois et déployé deux applications node.js sur le serveur, puis chaque application a ouvert N-1 processus. En conséquence, ils se font concurrence pour le processeur, ce qui entraîne une forte augmentation de la charge du système. Bien que nos serveurs soient tous des machines à 8 cœurs, la surcharge de performances causée par le changement de contexte peut toujours être clairement ressentie. Le changement de contexte fait référence au phénomène par lequel le processeur suspend la tâche en cours afin d'effectuer d'autres tâches. Lors du changement, le noyau doit suspendre tout état du processus en cours, puis charger et exécuter un autre processus. Pour résoudre ce problème, nous avons réduit le nombre de processus démarrés par chaque application afin qu'elles puissent partager équitablement le CPU. En conséquence, la charge du système a été réduite :
.
Veuillez prêter attention à l'image ci-dessus pour voir comment la charge du système (ligne bleue) descend en dessous du nombre de cœurs de processeur (ligne rouge). Sur d'autres serveurs, nous avons constaté la même chose. Étant donné que la charge de travail totale reste la même, l'amélioration des performances dans le graphique ci-dessus ne peut être attribuée qu'à la réduction des changements de contexte.