"Ne bloquez pas la boucle événementielle..." - vous avez probablement entendu cette phrase de nombreuses fois... Je ne suis pas surpris car c'est l'une des hypothèses les plus importantes lorsque l'on travaille avec Node. Mais il y a aussi une deuxième "chose" que vous devriez vous abstenir de bloquer - le Worker Pool. S'il est négligé, il peut avoir un impact significatif sur les performances de l'application et même sur sa sécurité.
Fils
Principale chose à retenir : il y a deux types de fils dans Node.js: Main Thread - qui est géré par Boucle d'événementset Réserve de travailleurs (thread pool) - qui est le pool de threads - (threads pool)
grâce à libuv. Chacun d'entre eux a une tâche différente. L'objectif du premier est de gérer les opérations d'E/S non bloquantes, et le second est responsable des travaux intensifs de l'unité centrale et des E/S bloquantes.
![composants du libuv diagramme de libuv](/app/uploads/2024/05/libuv.png)
Mais qu'est-ce qu'un thread et en quoi est-il différent d'un processus ? Il y a plusieurs différences, mais la plus importante pour nous est la manière dont la mémoire leur est allouée. Vous pouvez considérer un processus comme une application. À l'intérieur de chaque processus, il y a un morceau de mémoire dédié uniquement à ce processus. Ainsi, un processus n'a pas accès à la mémoire du second, et cette propriété garantit une grande sécurité. Pour établir une communication entre eux, nous devons effectuer un certain travail. Les threads sont différents. Les threads s'exécutent à l'intérieur d'un processus et partagent la même mémoire ; il n'y a donc aucun problème à ce que les threads partagent des données.
Cependant, un point pose problème. Il s'agit de la condition de course. Les threads peuvent s'exécuter en même temps, alors comment savoir lequel se termine en premier ? Il se peut que la première fois que vous l'exécutez, la première opération se termine en premier, et que la fois suivante, ce soit l'inverse et que la deuxième opération se termine avant la première. Imaginez que vous travailliez avec des opérations d'écriture/lecture dans de telles conditions ! Un cauchemar ! Il est parfois très difficile d'écrire des code dans un environnement multithread.
De plus, les langages multithreads ont une surcharge de mémoire importante parce qu'ils créent un thread séparé pour chaque requête ; ainsi, si vous voulez appeler 1000 requêtes, ils créent 1000 threads.
Comment résoudre ce problème ? Utilisez plutôt un fil unique ! Et c'est ce que Nœud vous offre.
![diagramme de boucle d'événements boucle événementielle à un seul fil](/app/uploads/2024/05/eventloop.png)
En tant que JavaScript développeur Je vous encourage à regarder le film
dans lequel Bart Belder explique clairement le concept de boucle d'événements. Le diagramme ci-dessus est extrait de sa présentation. Et si vous ne connaissez pas du tout ces termes, vous pouvez consulter les deux documents suivants Nœud et Libuv disposent d'une excellente documentation 🙂 .
A propos du blocage
En JavaScript développement l'industrie, ils disent que parce que Nœud est monotâche et non bloquant, vous pouvez atteindre une plus grande concurrence avec les mêmes ressources qu'avec des solutions multithreads. C'est vrai, mais ce n'est pas aussi beau et facile qu'il n'y paraît.
Depuis Node.js est monotâche (partie JS), les tâches à forte intensité de CPU bloqueront toutes les requêtes en cours jusqu'à ce que la tâche en question soit achevée. Il est donc vrai qu'en Node.js vous pouvez bloquer toutes les requêtes simplement parce que l'une d'entre elles contient une instruction bloquante. Le code bloquant signifie qu'il faut plus de quelques millisecondes pour terminer. Mais il ne faut pas confondre temps de réponse long et blocage. La réponse de la base de données peut être très longue, mais elle ne bloque pas votre processus (application).
Les méthodes bloquantes s'exécutent de manière synchrone et les méthodes non bloquantes s'exécutent de manière asynchrone.
Comment ralentir (ou bloquer) votre boucle d'événements ?
- regex vulnérable - une expression régulière vulnérable est celle sur laquelle votre moteur d'expression régulière peut prendre un temps exponentiel ; vous pouvez en savoir plus à ce sujet. ici,
- Opérations JSON sur des objets volumineux,
- en utilisant les API synchrones de Nœud au lieu de versions asynchrones ; toutes les méthodes d'E/S de la bibliothèque standard Node.js fournissent également leurs versions asynchrones,
- d'autres erreurs de programmation, comme les boucles infinies synchrones.
Dans ce cas, puisque le Worker Pool utilise un pool de threads, est-il possible de les bloquer également ? Malheureusement, oui 🙁 Nœud se fonde sur une philosophie un seul fil pour plusieurs clients.
Supposons qu'une tâche donnée exécutée par un Worker spécifique soit très complexe et nécessite plus de temps pour être terminée. En conséquence, le Worker est bloqué et ne peut être utilisé pour exécuter aucune des autres tâches en attente jusqu'à ce que ses instructions soient exécutées. Comme vous l'avez probablement deviné, cela peut affecter les performances. Vous pouvez éviter de tels problèmes en minimisant la variation des durées des tâches en utilisant le partitionnement des tâches.
Conclusion
Évitez le blocage, c'est certain. Si vous le pouvez, choisissez toujours des versions asynchrones des API de la bibliothèque standard. Sinon, après l'exécution de votre application, le client peut rencontrer plusieurs problèmes, en commençant par la dégradation du débit et en finissant par un retrait complet, ce qui est fatal du point de vue de l'utilisateur.
En savoir plus :
Pourquoi vous devriez (probablement) utiliser Typescript
Comment ne pas tuer un projet avec de mauvaises pratiques de codage ?
Stratégies de récupération des données dans NextJS