J'ai donc une compréhension du fonctionnement de Node.js: il a un seul thread d'écoute qui reçoit un événement et le délègue ensuite à un pool de travailleurs. Le thread de travail notifie l'écouteur une fois qu'il a terminé le travail, et l'écouteur renvoie ensuite la réponse à l'appelant.
Ma question est la suivante: si je lève un serveur HTTP dans Node.js et que j'appelle sleep sur l'un de mes événements de chemin routés (comme "/ test / sleep"), tout le système s'arrête. Même le fil d'écoute unique. Mais je crois comprendre que ce code se produit sur le pool de travailleurs.
Maintenant, en revanche, lorsque j'utilise Mongoose pour parler à MongoDB, les lectures de bases de données sont une opération d'E / S coûteuse. Node semble être capable de déléguer le travail à un thread et de recevoir le rappel lorsqu'il se termine; le temps de chargement à partir de la base de données ne semble pas bloquer le système.
Comment Node.js décide-t-il d'utiliser un thread de pool de threads par rapport au thread d'écoute? Pourquoi ne puis-je pas écrire de code d'événement qui dort et ne bloque qu'un thread de pool de threads?
Réponses:
Votre compréhension du fonctionnement du nœud n'est pas correcte ... mais c'est une idée fausse courante, car la réalité de la situation est en fait assez complexe et se résume généralement à de petites phrases lapidaires comme "le nœud est à un seul thread" qui simplifient à l'extrême les choses .
Pour le moment, nous ignorerons le multi-traitement / multi-thread explicite via le cluster et les threads de webworker , et parlerons simplement du nœud non threadé typique.
Le nœud s'exécute dans une seule boucle d'événements. C'est un thread unique, et vous n'obtenez jamais qu'un seul 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, alors elle bloquera la boucle entière et rien d'autre ne se produira jusqu'à ce qu'elle se termine. Il s'agit de la nature typiquement à thread unique du nœud dont vous entendez tant parler. Mais ce n'est pas une vue d'ensemble.
Certaines fonctions et 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 le passage de l'appel à un thread de travail. Par exemple, lorsque vous utilisez le
fs
module pour demander un fichier, lefs
module transmet cet appel à un thread de travail, et ce worker attend sa réponse, qu'il présente ensuite à la boucle d'événements qui s'est déclenchée sans elle dans le entre temps. Tout cela est soustrait à vous, le développeur du nœud, et une partie est soustraite aux développeurs de modules grâce à l'utilisation de libuv .Comme le souligne 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
http
module une stratégie différente semble être utilisé à ce moment. Pour nos besoins ici, il est principalement 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'asynchronicité.Sur une tangente principalement liée, il y a une analyse beaucoup plus profonde de la façon dont le nœud atteint l'asynchronicité, et de certains problèmes potentiels connexes et comment les traiter, dans cet excellent article . La plus grande partie se développe sur ce que j'ai écrit ci-dessus, mais en plus, cela indique:
UV_THREADPOOL_SIZE
variable d'environnement, tant que vous le faites avant que le pool de threads ne soit requis et créé:process.env.UV_THREADPOOL_SIZE = 10;
Si vous voulez un multi-traitement ou un multi-threading traditionnel dans le nœud, vous pouvez l'obtenir via le
cluster
module intégré ou divers autres modules tels que ceux mentionnés ci-dessuswebworker-threads
, ou vous pouvez le simuler en mettant en œuvre un moyen de segmenter votre travail et en utilisant manuellementsetTimeout
ousetImmediate
ouprocess.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é).Veuillez noter que si vous écrivez du code long / bloquant en javascript, vous faites probablement une erreur. D'autres langues fonctionneront beaucoup plus efficacement.
la source
Ce n'est pas vraiment exact. Node.js n'a qu'un seul thread "de travail" qui exécute javascript. Il y a des threads dans le nœud qui gèrent le traitement des E / S, mais les considérer comme des «travailleurs» est une idée fausse. Il n'y a vraiment que la gestion des E / S et quelques autres détails sur l'implémentation interne du nœud, mais en tant que programmeur, vous ne pouvez pas influencer leur comportement à part quelques paramètres divers tels que MAX_LISTENERS.
Il n'y a pas de mécanisme de veille en JavaScript. Nous pourrions en discuter plus concrètement si vous publiez un extrait de code de ce que vous pensez que "dormir" signifie. Il n'y a pas une telle fonction à appeler pour simuler quelque chose comme
time.sleep(30)
en python, par exemple. Il y asetTimeout
mais ce n'est fondamentalement PAS le sommeil.setTimeout
et libérersetInterval
explicitement , et non bloquer, la boucle d'événements afin 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 de boucler le CPU avec un calcul en mémoire, ce qui affamera en effet le thread d'exécution principal et rendra votre programme insensible.Le réseau IO est toujours asynchrone. Fin de l'histoire. Disk IO a à la fois des API synchrones et asynchrones, il n'y a donc pas de «décision». node.js se comportera selon les fonctions principales de l'API que vous appelez sync vs async normal. Par exemple:
fs.readFile
vsfs.readFileSync
. Pour les processus enfants, il y a aussi séparéschild_process.exec
etchild_process.execSync
API.La règle d'or est toujours d'utiliser les API asynchrones. Les raisons valables d'utiliser les API de synchronisation sont pour le code d'initialisation dans un service réseau avant qu'il n'écoute les connexions ou dans des scripts simples qui n'acceptent pas les demandes réseau pour les outils de construction et ce genre de chose.
la source
fs
, pour autant que je sacheThread pool comment quand et qui a utilisé:
Tout d'abord, lorsque nous utilisons / installons Node sur un ordinateur, il démarre un processus parmi d'autres processus qui est appelé processus de nœud dans l'ordinateur, et il continue de fonctionner jusqu'à ce que vous le tuiez. Et ce processus en cours est notre soi-disant thread unique.
Donc, le mécanisme de thread unique facilite le blocage d'une application de nœud, mais c'est l'une des fonctionnalités uniques que Node.js apporte à la table. Donc, encore une fois, si vous exécutez votre application de nœud, elle ne fonctionnera que dans un seul thread. Peu importe si vous avez 1 ou un million d'utilisateurs accédant à votre application en même temps.
Comprenons donc exactement ce qui se passe dans le thread unique de nodejs lorsque vous démarrez votre application de nœud. Au début, le programme est initialisé, puis tout le code de niveau supérieur est exécuté, ce qui signifie que tous les codes qui ne sont dans aucune fonction de rappel ( rappelez-vous que tous les codes à l'intérieur de toutes les fonctions de rappel seront exécutés sous la boucle d'événement ).
Après cela, tous les modules code exécutés puis enregistrent tous les rappels, enfin, la boucle d'événement lancée pour votre application.
Ainsi, comme nous le verrons avant, toutes les fonctions de rappel et les codes à l'intérieur de ces fonctions s'exécuteront sous une boucle d'événement. Dans la boucle d'événements, les charges sont réparties en différentes phases. Quoi qu'il en soit, je ne vais pas discuter de la boucle d'événements ici.
Eh bien, pour mieux comprendre le pool de threads, je vous demande d'imaginer que dans la boucle d'événements, les codes à l'intérieur d'une fonction de rappel s'exécutent après avoir terminé l'exécution de codes dans une autre fonction de rappel, maintenant s'il y a des tâches en fait trop lourdes. Ils bloqueraient alors notre thread unique nodejs. Et donc, c'est là que le pool de threads entre en jeu, qui est tout comme la boucle d'événements, est fourni à Node.js par la bibliothèque libuv.
Ainsi, le pool de threads ne fait pas partie de nodejs lui-même, il est fourni par libuv pour décharger de lourdes tâches sur libuv, et libuv exécutera ces codes dans ses propres threads et après l'exécution, libuv retournera les résultats à l'événement dans la boucle d'événements.
Le pool de threads nous donne quatre threads supplémentaires, ceux-ci sont complètement séparés du thread unique principal. Et nous pouvons en fait le configurer jusqu'à 128 threads.
Donc, tous ces threads forment ensemble un pool de threads. et la boucle d'événements peut alors automatiquement décharger les tâches lourdes vers le pool de threads.
La partie amusante est que tout cela se produit automatiquement dans les coulisses. Ce ne sont pas nous les développeurs qui décidons de ce qui va au pool de threads et de ce qui ne l'est pas.
Il existe de nombreuses tâches qui vont au pool de threads, telles que
la source
Ce malentendu n'est que la différence entre le multitâche préventif et le multitâche coopératif ...
Le sommeil éteint tout le carnaval car il n'y a vraiment qu'une seule file d'attente pour tous 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 bloquez pas.
la source