Je comprends donc le fonctionnement de Node.js: il possède un seul thread d’écoute qui reçoit un événement, puis le délègue à un pool de travail. Le thread de travail notifie l’auditeur une fois le travail terminé, et l’auditeur retourne ensuite la réponse à l’appelant.
Ma question est la suivante: si je place un serveur HTTP dans Node.js et que j’appelle sleep sur l’un de mes événements de chemin routés (par exemple “/ test / sleep”), tout le système s’arrête. Même le fil de l’auditeur unique. Mais d’après ce que j’ai compris, ce code est en cours d’exécution sur le pool de travail.
En revanche, lorsque j’utilise Mongoose pour parler à MongoDB, les lectures de firebase database constituent une opération d’E / S coûteuse. Node semble pouvoir déléguer le travail à un thread et recevoir le rappel lorsqu’il est terminé; le temps nécessaire pour charger depuis la firebase database ne semble pas bloquer le système.
Comment Node.js décide-t-il d’utiliser un thread de pool de threads plutôt que le thread d’écoute? Pourquoi ne puis-je pas écrire le code d’événement qui dort et ne bloque qu’un thread de pool de threads?
Votre compréhension de la façon dont fonctionne le nœud n’est pas correcte … mais c’est une idée fausse commune, car la réalité de la situation est en réalité assez complexe, et se résume généralement à des petites phrases comme “nœud est un seul thread” qui simplifie les choses .
Pour le moment, nous ignorerons le multi-traitement / multi-thread explicite à travers les threads de cluster et de travail en ligne, et nous parlerons simplement d’un noeud non-threadé typique.
Le nœud s’exécute dans une boucle d’événement unique. C’est un thread unique, et vous ne recevez que ce thread. Tout le javascript que vous écrivez s’exécute dans cette boucle, et si une opération de blocage se produit dans ce code, cela bloquera toute la boucle et rien d’autre ne se produira avant la fin. Il s’agit de la nature généralement unique d’un nœud dont vous entendez tant parler. Mais ce n’est pas toute l’image.
Certaines fonctions et certains modules, généralement écrits en C / C ++, prennent en charge les E / S asynchrones. Lorsque vous appelez ces fonctions et méthodes, elles gèrent en interne la transmission de l’appel à un thread de travail. Par exemple, lorsque vous utilisez le module fs
pour demander un fichier, le module fs
transmet cet appel à un thread de travail et ce dernier attend sa réponse, qu’il présente ensuite à la boucle d’événement qui se déroule sans lui. pendant ce temps. Tout cela est éloigné de vous, le développeur de noeuds, et une partie de celui-ci est éloigné des développeurs de modules grâce à l’utilisation de libuv .
Comme l’a souligné Denis Dollfus dans les commentaires (de cette réponse à une question similaire), la stratégie utilisée par libuv pour réaliser des E / S asynchrones n’est pas toujours un pool de threads, en particulier dans le cas du module http
être utilisé à ce moment. Pour notre propos, il est important de noter comment le contexte asynchrone est atteint (en utilisant libuv) et que le pool de threads maintenu par libuv est l’une des multiples stratégies proposées par cette bibliothèque pour atteindre l’asynchronisme.
Dans un excellent article , il existe une parsing beaucoup plus approfondie de la manière dont le nœud atteint l’asynchronisme et de certains problèmes potentiels connexes, ainsi que de la façon de les traiter. La plupart de ces informations développent ce que j’ai écrit ci-dessus, mais en plus il fait remarquer:
UV_THREADPOOL_SIZE
, tant que vous le faites avant que le pool de threads ne soit requirejs et créé: process.env.UV_THREADPOOL_SIZE = 10;
Si vous voulez un multi-traitement traditionnel ou un multi-threading dans un nœud, vous pouvez le faire via le module de cluster
intégré ou divers autres modules tels que les webworker-threads
mentionnés ci webworker-threads
dessus. et manuellement en utilisant setTimeout
ou setImmediate
ou process.nextTick
pour suspendre votre travail et le poursuivre dans une boucle ultérieure pour laisser les autres processus se terminer (mais ce n’est pas recommandé).
S’il vous plaît noter que si vous écrivez long code de javascript / en cours d’exécution, vous faites probablement une erreur. Les autres langues fonctionneront beaucoup plus efficacement.
Je comprends donc le fonctionnement de Node.js: il possède un seul thread d’écoute qui reçoit un événement, puis le délègue à un pool de travail. Le thread de travail notifie l’auditeur une fois le travail terminé, et l’auditeur retourne ensuite la réponse à l’appelant.
Ce n’est pas vraiment précis. Node.js n’a qu’un seul thread “worker” qui exécute javascript. Il existe des threads au sein du nœud qui gèrent le traitement des entrées-sorties, mais les considérer comme des “travailleurs” est une idée fausse. Il y a vraiment juste la gestion des entrées-sorties et quelques autres détails de l’implémentation interne des nœuds, mais en tant que programmeur, vous ne pouvez pas influencer leur comportement à part quelques parameters différents tels que MAX_LISTENERS.
Ma question est la suivante: si je place un serveur HTTP dans Node.js et que j’appelle sleep sur l’un de mes événements de chemin routés (par exemple “/ test / sleep”), tout le système s’arrête. Même le fil de l’auditeur unique. Mais d’après ce que j’ai compris, ce code est en cours d’exécution sur le pool de travail.
Il n’y a pas de mécanisme de sumil en JavaScript. Nous pourrions en discuter plus concrètement si vous avez posté un extrait de code de ce que vous pensez que “sumil” signifie. Il n’y a pas une telle fonction à appeler pour simuler quelque chose comme time.sleep(30)
dans python, par exemple. Il y a setTimeout
mais ce n’est PAS fondamentalement sumil. setTimeout
et setInterval
libèrent explicitement, et non bloquent, la boucle d’événement pour que d’autres bits de code puissent s’exécuter sur le thread d’exécution principal. La seule chose que vous pouvez faire est d’occuper en boucle le processeur avec un calcul en mémoire, ce qui va effectivement affamer le thread d’exécution principal et rendre votre programme sans réponse.
Comment Node.js décide-t-il d’utiliser un thread de pool de threads plutôt que le thread d’écoute? Pourquoi ne puis-je pas écrire le code d’événement qui dort et ne bloque qu’un thread de pool de threads?
Le réseau IO est toujours asynchrone. Fin de l’histoire. Le disque IO possède à la fois des API synchrones et asynchrones, il n’y a donc pas de “décision”. node.js se comportera conformément aux fonctions principales de l’API que vous appelez sync vs async normal. Par exemple: fs.readFile
vs fs.readFileSync
. Pour les processus enfants, il existe également des API child_process.exec
et child_process.execSync
.
La règle de base est d’utiliser toujours les API asynchrones. Les raisons valables d’utiliser les API de synchronisation concernent le code d’initialisation dans un service réseau avant d’écouter les connexions ou dans des scripts simples qui n’acceptent pas les requêtes réseau pour les outils de génération et ce genre de choses.
Ce malentendu n’est que la différence entre le multitâche préventif et le multitâche coopératif …
Le sumil éteint tout le carnaval car il y a vraiment une ligne pour toutes les manèges et vous avez fermé la porte. Pensez-y comme “un interpréteur JS et d’autres choses” et ignorez les threads … pour vous, il n’y a qu’un seul thread, …
… alors ne le bloque pas.