343 votes

Comment le modèle IO non bloquant fileté unique fonctionne en Node.js

Je ne suis pas un Node programmeur, mais je suis intéressé par la façon dont le single threaded non bloquant IO modèle fonctionne. Cependant, après la lecture de cet article de comprendre-les-node-js-événement en boucle, je suis vraiment confus à ce sujet.

Il a donné un exemple pour le modèle:

c.query(
   'SELECT SLEEP(20);',
   function (err, results, fields) {
     if (err) {
       throw err;
     }
     res.writeHead(200, {'Content-Type': 'text/html'});
     res.end('<html><head><title>Hello</title></head><body><h1>Return from async DB query</h1></body></html>');
     c.end();
    }
);

Voici ma question:

Lorsqu'il y a deux demandes d'Une(première) et B), puisqu'il n'existe qu'un seul thread, le côté serveur programme permettra de répondre à la demande d'Une première. Faire SQL d'interrogation, qui est essentiellement une instruction de mise en veille permanent pour les e/S en attente. Le programme est "coincé" dans l'attente d'e/S, et ne peut pas exécuter le code qui affiche la page web.

Le programme de l'interrupteur de demande de B pendant l'attente?

À mon avis, parce que c'est un seul modèle de thread, il n'y a aucun moyen de passer de l'un demande à l'autre. Mais le titre de l'exemple de code dit que "tout se déroule en parallèle à l'exception de votre code".

(P. S je ne suis pas sûr si j'ai mal compris le code ou pas puisque je n'ai jamais utilisé Node.)

Comment passer d'Un Nœud à B pendant l'attente? Et pouvez-vous expliquer la mono-thread non bloquant IO modèle d' Node d'une manière simple?

Je vous serais reconnaissant si vous pouviez m'aider. :)

394voto

Utaal Points 2063

Node.js elle est construite sur libuv, une croix-plate-forme de bibliothèque que les résumés des api/appels asynchrones (non bloquant) d'entrée/de sortie fourni par les prises en charge des Systèmes d'exploitation (Unix, mac OS X et Windows au moins).

Asynchrone IO

Dans ce modèle de programmation open/opération de lecture/écriture sur les périphériques et les ressources (sockets, système de fichiers, etc.) géré par le système de fichiers à ne pas bloquer le thread appelant (comme dans le type synchrone c-comme modèle) et il suffit de marquer le processus (dans kernel/niveau de l'OS, la structure de données) pour être averti lorsque de nouvelles données ou des événements sont disponibles. Dans le cas d'un serveur web-comme l'app, le processus est alors responsable de la figure qui demande/contexte de la notification de l'événement appartient, et de poursuivre le traitement de la demande à partir de là. Notez que cela signifie nécessairement que vous serez sur un autre bloc de pile de celui à l'origine de la demande pour le système d'exploitation que celui-ci avait céder à un processus de " répartiteur pour un seul thread processus pour gérer les nouveaux événements.

Le problème avec le modèle que j'ai décrit, c'est qu'il n'est pas familier et dur à comprendre pour le programmeur que c'est non-séquentielle dans la nature. "Vous devez faire la demande en fonction de A et de gérer le résultat dans une autre fonction de l'endroit où votre des habitants de la Une ne sont généralement pas disponibles."

Le nœud du modèle (en Continuation Passing Style et la Boucle d'Événement)

Nœud aborde le problème profit javascript est le langage de fonctionnalités pour faire de ce modèle un peu plus synchrone à la recherche en induisant le programmeur à employer un certain style de programmation. Chaque fonction que les demandes IO a une signature comme function (... parameters ..., callback) et a donc besoin d'un rappel qui sera appelée lorsque l'opération demandée est terminé (gardez à l'esprit que la plupart du temps est passé à attendre pour le système d'exploitation pour signaler l'achèvement en temps qui peut être consacré à d'autres travaux). Javascript support pour des fermetures vous permet d'utiliser des variables que vous avez définies dans l'extérieur (appel de fonction) à l'intérieur du corps de la fonction de rappel - cela permet de maintenir l'état entre les différentes fonctions qui seront appelées par le nœud d'exécution de manière indépendante. Voir aussi la Continuation Passing Style.

En outre, après l'appel d'une fonction de frai d'une opération e / s la fonction d'appel habituellement return de contrôle au nœud de la boucle d'événements. Cette boucle va appeler la fonction de rappel ou de la fonction qui était prévue pour l'exécution (probablement parce que l'événement correspondant a été notifiée par le système d'exploitation), ce qui permet le traitement simultané de plusieurs demandes.

Vous pouvez penser nœud de boucle d'évènements comme un peu comme le noyau du répartiteur: le noyau devrait calendrier pour l'exécution d'un thread bloqué une fois en instance IO est terminé alors que le nœud de planifier un rappel lorsque l'événement a eu lieu.

Très simultanées, pas de parallélisme

Comme dernière remarque, le membre de phrase "tout fonctionne en parallèle à l'exception de votre code" ne fait un travail décent de capturer le point de ce nœud permet à votre code pour traiter les demandes des centaines de milliers d'ouvrir le socket avec un seul thread en même temps par le multiplexage et le séquençage de l'ensemble de vos js logique dans un seul flux d'exécution (même si en disant: "tout ce qui s'exécute en parallèle" n'est probablement pas correct ici - voir la Simultanéité vs Parallélisme - Quelle est la différence?). Cela fonctionne assez bien pour webapp serveurs comme la plupart du temps est effectivement passé en attente pour le réseau ou le disque (base de données / sockets) et la logique n'est pas vraiment l'UC - c'est-à-dire: cela fonctionne bien pour les IO-tenu des charges de travail.

219voto

user568109 Points 21253

Bien de donner un peu de perspective permettez-moi de comparer node.js avec apache.

Apache est un multi-thread serveur HTTP, pour chacun et pour chaque requête que le serveur reçoit, il crée un thread qui s'occupe de cette demande.

Node.js sur l'autre main event driven, qui traite toutes les requêtes de manière asynchrone à partir d'un seul thread.

Lorsque A et B sont reçus sur apache, deux fils sont créés de traiter les demandes. Chaque manipulation de la requête séparément, chacun d'attente pour les résultats de la requête avant de servir la page. La page n'est servie jusqu'à ce que la requête est terminée. La requête d'extraction bloque parce que le serveur ne peut pas exécuter le reste de fil jusqu'à ce qu'il reçoit le résultat.

Dans le nœud c.requête est traitée de manière asynchrone, ce qui signifie que, tandis que le c.requête extrait les résultats, il saute de la poignée c.requête pour B, et quand les résultats arrivent pour Une arrivée il renvoie les résultats de callback qui envoie la réponse. Node.js sait pour exécuter de rappel lors de l'extraction de finitions.

À mon avis, parce que c'est un seul modèle de thread, il n'y a aucun moyen de passer de l'un demande à l'autre.

En fait le nœud du serveur est exactement ce que fait pour vous tout le temps. Pour faire des commutateurs, (le comportement asynchrone) la plupart des fonctions que vous utilisez aura des rappels.

Modifier

La requête SQL est pris à partir de mysql bibliothèque. Il met en œuvre rappel de style ainsi que des événements de l'émetteur à la file d'attente des requêtes SQL. Il n'a pas à les exécuter de manière asynchrone, c'est fait en interne par les libuv fils qui fournissent l'abstraction de non-blocage I/O. Les étapes suivantes arriver pour faire une requête :

  1. Ouvrir une connexion à la db, la connexion en elle-même peut être fait de manière asynchrone.
  2. Une fois que db est connecté, la requête est transmise au serveur. Les requêtes peuvent être mis en file d'attente.
  3. La boucle principale est notifié de la fin avec rappel ou d'un événement.
  4. Boucle principale exécute votre rappel/gestionnaire d'événements.

Les requêtes au serveur http sont traitées de la même manière. Le filetage interne, l'architecture est quelque chose comme ceci :

node.js event loop

Le C++ threads sont les libuv ceux qui ne le asynchronous I/O (sur disque ou réseau). La boucle principale continue à exécuter après l'envoi de la demande de pool de threads. Il peut accepter plus de demandes qu'il n'attend pas ou dormir. Les requêtes SQL/requêtes HTTP/système de fichiers lit tous se passent de cette manière.

6voto

phoenix Points 65

La fonction c.query() a deux argument

c.query("Fetch Data", "Post-Processing of Data")

L'opération "récupération de Données" dans ce cas est un DB-Requête, maintenant, cela peut être traitée par Node.js par le frai arrêt d'un thread de travail et de lui donner cette tâche à effectuer les DB-Requête. (Rappelez-vous Node.js peut créer de thread en interne). Cela permet à la fonction de renvoyer instantanément sans aucun délai

Le deuxième argument de "Post-Traitement de Données" est une fonction de rappel, le nœud cadre registres de ce rappel et est appelée par la boucle d'événements.

Ainsi, l'énoncé c.query (paramenter1, parameter2) sera de retour instantanément, permettant nœud pour répondre à une autre demande.

P. S: je viens de commencer à comprendre nœud, en fait, je voulais écrire ce que le commentaire de @Philippe , mais depuis n'avais pas assez de points de réputation ainsi écrivait-il comme réponse.

4voto

usr Points 74796

Comment passer d'Un Nœud à B pendant l'attente?

c.query retourne immédiatement (pas de "sommeil"). L'exécution de la requête "dans le fond". D'autres demandes peuvent maintenant exécuter et émettre des requêtes elles-mêmes. Votre code est appelé en arrière une fois l'attente est terminée.

C'est pourquoi les rappels sont là. Cela ne pourrait pas fonctionner sans les rappels, car query ne peut pas retourner à la fois immédiatement et avec les résultats. Si vous n'avez pas de tirer pleinement parti de cette dernière phrase, répondre à la question suivante: Comment cela pourrait-il fonctionner sans suites/rappels? Vous ne pouvez pas trouver une réponse.

3voto

Gal Ben-Haim Points 5702

si vous lisez un peu plus loin - "bien sûr, sur le backend, il y a des threads et de processus pour la DB d'accès et d'exécution des processus. Cependant, ce ne sont pas explicitement exposées de votre code, de sorte que vous pouvez vous inquiétez pas autrement qu'en sachant que I/O par exemple, les interactions avec la base de données, ou avec d'autres processus sera asynchrone du point de vue de la demande, puisque les résultats de ces discussions sont renvoyés via la boucle d'événement de votre code."

sur - "tout se déroule en parallèle à l'exception de votre code" - votre code est exécuté en mode synchrone, chaque fois que vous lancez une opération asynchrone comme en attente pour IO, la boucle d'événements s'occupe de tout et appelle le callback. il tout simplement pas quelque chose que vous avez à penser.

dans votre exemple: il y a deux demandes d'Une (première) et B. vous l'exécution de la demande d'Un, votre code de continuer à fonctionner de manière synchrone et l'exécution de la demande B. la boucle d'événement de poignées de demander Un, quand il a finit il appelle le rappel de la demande avec le résultat, en va de même pour la demande de B.

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