145 votes

Boucle d'événements Nodejs

Y a-t-il deux boucles d'événements internes dans l'architecture nodejs ?

  • libev/libuv
  • v8 javascript boucle d'événement

Lors d'une demande d'E/S, le nœud met-il en file d'attente la demande à la libeio qui, à son tour, notifie la disponibilité des données via des événements utilisant la libev et, finalement, ces événements sont traités par la boucle d'événements de la v8 en utilisant des callbacks ?

Fondamentalement, comment libev et libeio sont-elles intégrées dans l'architecture nodejs ?

Existe-t-il une documentation disponible pour donner une image claire de l'architecture interne de nodejs ?

180voto

darkyen00 Points 1786

J'ai personnellement lu le code source de node.js et v8.

J'ai rencontré un problème similaire au vôtre lorsque j'ai essayé de comprendre l'architecture de node.js afin d'écrire des modules natifs.

Ce que je poste ici est ma compréhension de node.js et il se peut que cela soit un peu hors sujet.

  1. Libev est la boucle d'événement qui s'exécute en interne dans node.js pour effectuer des opérations simples de boucle d'événement. Elle a été écrite à l'origine pour les systèmes *nix. Libev fournit une boucle d'événements simple mais optimisée pour l'exécution du processus. Vous pouvez en savoir plus sur libev aquí .

  2. LibEio est une bibliothèque permettant d'effectuer des entrées-sorties de manière asynchrone. Elle gère les descripteurs de fichiers, les gestionnaires de données, les sockets, etc. Vous pouvez en savoir plus à ce sujet ici aquí .

  3. LibUv est une couche d'abstraction au-dessus de libeio , libev, c-ares (pour le DNS) et iocp (pour l'EDI asynchrone de Windows). LibUv réalise, maintient et gère toutes les entrées/sorties et les événements dans le pool d'événements (dans le cas de libeio threadpool). Vous devriez consulter Le tutoriel de Ryan Dahl sur libUv. Vous commencerez à mieux comprendre le fonctionnement de la libUv elle-même, puis vous comprendrez comment node.js fonctionne par-dessus la libuv et la v8.

Pour comprendre simplement la boucle d'événement javascript, vous devriez envisager de regarder ces vidéos

Pour voir comment libeio est utilisé avec node.js afin de créer des modules asynchrones, vous devriez voir cet exemple .

En gros, ce qui se passe à l'intérieur de node.js, c'est que la boucle v8 s'exécute et gère toutes les parties javascript ainsi que les modules C++ [ lorsqu'ils s'exécutent dans un thread principal ( selon la documentation officielle, node.js lui-même est mono thread) ]. En dehors du thread principal, libev et libeio le gèrent dans le pool de threads et libev assure l'interaction avec la boucle principale. Donc, d'après ce que j'ai compris, node.js n'a qu'une seule boucle d'événement permanente : c'est la boucle d'événement v8. Pour gérer les tâches asynchrones C++, il utilise un pool de threads [via libeio et libev].

Par exemple :

eio_custom(Task,FLAG,AfterTask,Eio_REQUEST);

qui apparaît dans tous les modules est généralement l'appel de la fonction Task dans le pool de threads. Lorsqu'il est terminé, il appelle la fonction AfterTask dans le fil principal. Alors que Eio_REQUEST est le gestionnaire de la demande qui peut être une structure / un objet dont la raison d'être est d'assurer la communication entre le threadpool et le thread principal.

0 votes

S'appuyer sur le fait que libuv utilise libev en interne est un bon moyen de rendre votre code non multiplateforme. Vous ne devriez vous préoccuper que de l'interface publique de libuv.

1 votes

@Raynos libuv vise à s'assurer que son x-platfousing de bibliothèques multiples . N'est-ce pas ? Utiliser libuv est donc une bonne idée.

1 votes

@Abhishek De Doc process.nextTick - Lors de la prochaine boucle autour de la boucle d'événement, appelez ce callback. Ce n'est pas un simple alias de setTimeout(fn, 0), c'est beaucoup plus efficace. À quelle boucle d'événement cela fait-il référence ? La boucle d'événement V8 ?

22voto

Gireesh Punathil Points 936

Il semble que certaines des entités discutées (ex : libev etc.) aient perdu de leur pertinence, du fait que cela fait un moment, mais je pense que la question a encore un grand potentiel.

Je vais essayer d'expliquer le fonctionnement du modèle piloté par les événements à l'aide d'un exemple abstrait, dans un environnement UNIX abstrait, dans le contexte de Node, à ce jour.

Le point de vue du programme :

  • Le moteur script lance l'exécution du script.
  • Chaque fois qu'une opération liée au CPU est rencontrée, elle est exécutée en ligne (machine réelle), dans son intégralité.
  • Chaque fois qu'une opération liée aux E/S est rencontrée, la demande et son gestionnaire d'achèvement sont enregistrés dans une "machine à événements" (machine virtuelle).
  • Répétez les opérations de la même manière ci-dessus jusqu'à ce que le script se termine. Opération liée au CPU - exécuter en ligne, celles liées aux E/S, demander à la machinerie comme ci-dessus.
  • Lorsque les E/S sont terminées, les auditeurs sont rappelés.

La machinerie événementielle ci-dessus s'appelle libuv AKA event loop framework. Node exploite cette bibliothèque pour mettre en œuvre son modèle de programmation piloté par les événements.

Le point de vue du nœud :

  • Un seul thread pour héberger le runtime.
  • Récupérer le script de l'utilisateur.
  • Compilez-le en natif [levier v8]
  • Chargez le binaire, et sautez dans le point d'entrée.
  • Le code compilé exécute les activités liées au CPU en ligne, en utilisant des primitives de programmation.
  • De nombreux codes liés aux entrées/sorties et aux temporisateurs ont des enveloppes natives. Par exemple, les entrées/sorties réseau.
  • Les appels d'E/S sont donc acheminés du script vers les ponts C++, avec le handle d'E/S et le gestionnaire d'achèvement passés comme arguments.
  • Le code natif exerce la boucle libuv. Il acquiert la boucle, met en file d'attente un événement de bas niveau qui représente l'E/S, et un wrapper de callback natif dans la structure de la boucle libuv.
  • Le code natif retourne au script - aucune E/S n'a lieu pour le moment !
  • Les points ci-dessus sont répétés plusieurs fois, jusqu'à ce que tout le code non-I/O soit exécuté, et que tout le code I/O soit enregistré dans la libuv.
  • Enfin, quand il n'y a plus rien à exécuter dans le système, le nœud passe le contrôle à la libuv
  • libuv entre en action, il récupère tous les événements enregistrés, interroge le système d'exploitation pour obtenir leur exploitabilité.
  • Ceux qui sont prêts pour les E/S dans un mode non bloquant, sont récupérés, les E/S effectuées et leurs rappels émis. L'un après l'autre.
  • Celles qui ne sont pas encore prêtes (par exemple une lecture de socket, pour laquelle l'autre point d'extrémité n'a encore rien écrit) continueront d'être sondées par le système d'exploitation jusqu'à ce qu'elles soient disponibles.
  • La boucle maintient en interne une minuterie toujours croissante. Lorsque l'application demande un rappel différé (comme setTimeout), la valeur de cette minuterie interne est utilisée pour calculer le bon moment pour lancer le rappel.

Si la plupart des fonctionnalités sont prises en charge de cette manière, certaines (versions asynchrones) des opérations sur les fichiers sont effectuées à l'aide de threads supplémentaires, bien intégrés dans la libuv. Alors que les opérations d'E/S du réseau peuvent attendre dans l'attente d'un événement externe, tel que la réponse de l'autre extrémité avec des données, etc. Par exemple, si vous ouvrez un fichier et attendez que le fd soit prêt avec des données, cela ne se produira pas, car personne n'est en train de lire ! Dans le même temps, si vous lisez le fichier en ligne dans le thread principal, cela peut potentiellement bloquer d'autres activités dans le programme, et peut créer des problèmes visibles, car les opérations de fichiers sont très lentes par rapport aux activités liées au processeur. Ainsi, des threads internes (configurables par la variable d'environnement UV_THREADPOOL_SIZE) sont utilisés pour opérer sur les fichiers, tandis que l'abstraction événementielle fonctionne intacte, du point de vue du programme.

J'espère que cela vous aidera.

0 votes

Comment savez-vous cela ? Pouvez-vous m'indiquer la source ?

18voto

zangw Points 401

Une introduction à libuv

El node.js a débuté en 2009 comme un environnement JavaScript découplé du navigateur. En utilisant le logiciel de Google V8 et celui de Marc Lehmann libev Avec l'arrivée de node.js, ce dernier combinait un modèle d'entrée/sortie événementiel avec un langage bien adapté à ce style de programmation, en raison de la manière dont il avait été façonné par les navigateurs. Comme node.js a gagné en popularité, il était important de le faire fonctionner sous Windows, mais libev ne fonctionnait que sous Unix. L'équivalent Windows des mécanismes de notification d'événements du noyau comme kqueue ou (e)poll est IOCP. libuv était une abstraction autour de libev ou d'IOCP selon la plate-forme, fournissant aux utilisateurs une API basée sur libev. Dans la version node-v0.9.0 de libuv libev a été supprimé .

Il y a aussi une image qui décrit la boucle d'événement dans Node.js par @. BusyRich


Mise à jour 05/09/2017

Selon ce document Boucle d'événements Node.js ,

Le schéma suivant donne un aperçu simplifié de l'ordre des opérations de la boucle d'événement.

>        timers         

       I/O callbacks     

       idle, prepare     

           incoming:   
           poll          <  connections, 
           data, etc.  

          check          

    close callbacks    

note : chaque boîte sera désignée comme une "phase" de la boucle de l'événement.

Aperçu des phases

  • minuteurs cette phase exécute les callbacks programmés par setTimeout() y setInterval() .
  • Rappels d'E/S : exécute presque tous les callbacks à l'exception de rappels de fermeture ceux qui sont programmés par des minuteries, et setImmediate() .
  • oisif, préparer : uniquement utilisé en interne.
  • sondage : récupérer les nouveaux événements d'E/S ; le nœud se bloquera ici si nécessaire.
  • vérifier : setImmediate() sont invoqués ici.
  • rappels de fermeture : par exemple socket.on('close', ...) .

Entre chaque exécution de la boucle d'événement, Node.js vérifie s'il attend des E/S asynchrones ou des temporisations et s'arrête proprement s'il n'y en a pas.

0 votes

Vous avez cité que " In the node-v0.9.0 version of libuv libev was removed "mais il n'y a pas de description à ce sujet dans nodejs changelog . github.com/nodejs/node/blob/master/CHANGELOG.md . Et si libev est supprimé, comment les E/S asynchrones sont-elles exécutées dans nodejs ?

0 votes

@intekhab, Per this enlace Je pense que la libuv basée sur la libeio pourrait être utilisée comme boucle d'événement dans node.js.

0 votes

@intekhab Je pense que libuv implémente toutes les fonctionnalités liées aux E/S et au polling : docs.libuv.org/fr/v1.x/loop.html

13voto

Peter Hauge Points 580

Il y a une boucle d'événement dans l'architecture NodeJs.

Modèle de boucle d'événement Node.js

Les applications Node fonctionnent selon un modèle événementiel à un seul fil. Cependant, Node met en œuvre un pool de threads en arrière-plan afin que le travail puisse être effectué.

Node.js ajoute du travail à une file d'attente d'événements, puis un seul thread exécutant une boucle d'événements le récupère. La boucle d'événements prend l'élément supérieur de la file d'attente d'événements, l'exécute, puis prend l'élément suivant.

Lors de l'exécution d'un code qui dure plus longtemps ou qui comporte des entrées/sorties bloquantes, au lieu d'appeler la fonction directement, il ajoute la fonction à la file d'attente des événements ainsi qu'une fonction de rappel qui sera exécutée une fois la fonction terminée. Lorsque tous les événements de la file d'attente d'événements de Node.js ont été exécutés, l'application Node.js se termine.

La boucle d'événements commence à rencontrer des problèmes lorsque les fonctions de notre application se bloquent sur les E/S.

Node.js utilise des rappels d'événements pour éviter d'avoir à attendre des E/S bloquantes. Par conséquent, toutes les requêtes qui effectuent des E/S bloquantes sont exécutées sur un thread différent en arrière-plan.

Lorsqu'un événement qui bloque les E/S est récupéré dans la file d'attente des événements, Node.js récupère un thread dans le pool de threads et exécute la fonction à cet endroit plutôt que sur le thread principal de la boucle d'événement. Cela permet d'éviter que les E/S bloquantes ne retardent le reste des événements de la file d'attente.

8voto

Warren Zhou Points 274

Il n'y a qu'une seule boucle d'événement fournie par libuv, V8 est juste un moteur d'exécution JS.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X