1. Introduction
Cet article présente les méthodes de base de développement de programmes à l'aide de flux Node.js.
<Code> "Nous devrions avoir quelques façons de connecter des programmes comme Garden Type - rédigeant un segment dans un autre segment lorsqu'il devient nécessaire de masser les données d'une autre manière. C'est aussi le moyen d'IO." Doug McIlroy. 11 octobre 1964 </code>
Le premier à entrer en contact avec Stream a été les décennies de pratique qui ont commencé avec le début Unix, qui a prouvé que les idées de flux peuvent simplement développer des systèmes énormes. Dans Unix, Stream est implémenté via |; Dans le nœud, en tant que module de flux intégré, de nombreux modules de base et des modules tripartites sont utilisés. Comme UNIX, le fonctionnement principal du flux de nœuds est .pipe (), et les utilisateurs peuvent utiliser le mécanisme de contre-pression pour contrôler l'équilibre entre la lecture et l'écriture.
Stream peut fournir aux développeurs une interface unifiée qui peut réutiliser et contrôler l'équilibre de lecture et d'écriture entre les flux via des interfaces de flux abstraites.
2. Pourquoi utiliser le flux
Les E / S dans le nœud sont asynchrones, donc la lecture et l'écriture sur le disque et le réseau nécessitent la lecture et l'écriture sur les données via des fonctions de rappel. Ce qui suit est un code simple pour un serveur de téléchargement de fichiers:
<code> var http = require ('http'); var fs = require ('fs'); var server = http.createServer (function (req, res) {fs.readfile (__ dirname + '/data.txt', function (err, data) {res.end (data);});});Ces codes peuvent implémenter les fonctions requises, mais le service doit mettre en mémoire l'ensemble des données de fichier avant d'envoyer les données du fichier. Si le fichier "data.txt" est grand et que la concurrence est grande, beaucoup de mémoire sera gaspillée. Étant donné que l'utilisateur doit attendre que l'ensemble du fichier soit mis en mémoire avant que les données du fichier puissent être acceptées, cela conduit à une très mauvaise expérience utilisateur. Heureusement, les deux paramètres (req, res) sont du flux, nous pouvons donc utiliser fs.creareAdStream () au lieu de fs.readfile ():
<code> var http = require ('http'); var fs = require ('fs'); var server = http.createServer (function (req, res) {var stream = fs.creareAdStream (__ dirname + '/data.txt'); stream.Pipe (res);}); server.liten (8000);La méthode .Pipe () écoute les événements «données» et «fin» de fs.crerareadStream (), de sorte que le fichier "data.txt" n'a pas besoin de mettre en cache le fichier entier. Une fois la connexion client terminée, un bloc de données peut être envoyé immédiatement au client. Un autre avantage de l'utilisation .Pipe () est qu'il peut résoudre le problème de la lecture et de l'écriture de déséquilibre causée par une très grande latence client. Si vous souhaitez compresser le fichier et l'envoyer, vous pouvez utiliser un module à trois partis pour l'implémenter:
<code> var http = require ('http'); var fs = require ('fs'); var oppressor = require ('oppressor'); var server = http.createServer (function (req, res) {var stream = fs.creareAdStream (__ dirname + '/data.txt'); stream.pipe (oppressor (req)). tuyDe cette façon, le fichier comprimera le navigateur qui prend en charge GZIP et dégonflera. Le module d'oppresseur gère tout le codage du contenu.
Stream rend les programmes de développement simples.
3. Concepts de base
Il y a cinq flux de base: lisibles, écrivains, transformés, duplex et «classiques».
3-1, pipe
Tous les types d'utilisation de flux .Pipe () Pour créer une paire d'entrée et de sortie, reçoivent un STREAT lisible SRC et publient ses données vers le flux écrivative DST, comme suit:
<code> src.pipe (dst) </code>
La méthode .Pipe (DST) renvoie le flux DST, de sorte que plusieurs .pipe () peuvent être utilisés successivement, comme suit:
<code> a.pipe (b) .Pipe (c) .Pipe (d) </code>
La fonction est la même que le code suivant:
<code> A.Pipe (b); b.Pipe (c); c.Pipe (d); </code>
3-2. Streams lisibles
En appelant la méthode .pipe () des flux lisibles, vous pouvez écrire les données de flux lisibles sur un flux de transformation ou de duplex écrivable.
<code> LEALABLESTREAM.PIPE (DST) </code>
1> Créer un flux lisible
Ici, nous créons un flux lisible!
<code> var lisibleable = require ('stream'). lisible; var rs = new lisiblenable; Rs.push ('bip'); Rs.push ('boop / n'); rs.push (null); rs.pipe (process.stout); $ node read0.jsbeep boop </code>Rs.push (null) informe le destinataire des données que les données ont été envoyées.
Notez que nous n'avons pas appelé Rs.Pipe (process.stdout); Avant de pousser toutes les données dans le flux lisible, mais toutes les données dans le flux lisible sont toujours complètement sorties, car le flux lisible cache toutes les données pressées avant que le récepteur ne lit les données. Mais dans de nombreux cas, une meilleure façon consiste à appuyer uniquement sur les données dans le flux lisible que lorsque les données reçoivent les données demandées au lieu de mettre en cache toutes les données. Réécrivons la fonction ._read () ci-dessous:
<code> var lisiblenable = require ('stream'). lisible; var rs = liseable (); var c = 97; rs._read = function () {Rs.push (string.fromCharcode (c ++)); if (c> 'z'.charcodeat (0)) rs.push (null);}; rs.Pipe (process.stdout); read1.jsabcdefghijklmnopqrstuvwxyz </code>Le code ci-dessus implémente la réécriture de la méthode _read () pour pousser les données dans le flux lisible que si le destinataire des données demande les données. La méthode _read () peut également recevoir un paramètre de taille qui indique la taille des données demandée des données, mais le flux lisible peut ignorer ce paramètre au besoin.
Notez que nous pouvons également utiliser util.inherits () pour hériter des flux lisibles. Pour illustrer que la méthode _read () est appelée uniquement lorsque le destinataire des données demande des données, nous faisons un retard lorsque nous poussons les données dans le flux lisible, comme suit:
<code> var lisibleable = require ('stream'). lisible; var rs = liseable (); var c = 97 - 1; rs._read = function () {if (c> = 'z'.charcodeat (0)) return rs.push (null); setTimeout (function () {rs.push (string.fromcharcode (++ c));},,, 100);}; Rs.Pipe (process.stdout); process.on ('exit', function () {console.error ('/ n_read () appelé' + (c - 97) + 'fois');}); process.stout.on ('error', process.exit); </code>Lors de l'exécution du programme avec la commande suivante, nous avons constaté que la méthode _read () n'était appelée que 5 fois:
<code> $ Node Read2.js | tête -c5abcde_read () appelé 5 fois </code>
La raison de l'utilisation d'une minuterie est que le système prend le temps d'envoyer des signaux pour informer le programme pour fermer le pipeline. Utilisez Process.stout.on ('Error', FN) pour gérer le système envoyant un signal SigPipe car la commande d'en-tête ferme le pipeline, car cela entraînera un processus. Si vous souhaitez créer un flux lisible qui peut être enfoncé dans n'importe quelle forme de données, définissez simplement le paramètre ObjectMode sur true lors de la création du flux, par exemple: lisible ({objectMode: true}).
2> Données de flux lisibles
Dans la plupart des cas, nous utilisons simplement la méthode du tuyau pour rediriger les données du flux lisible vers une autre forme de flux, mais dans certains cas, il peut être plus utile de lire les données directement à partir du flux lisible. comme suit:
<code> process.stdin.on ('lisible', function () {var buf = process.stdin.read (); console.dir (buf);}); $ (echo abc; sleep 1; echo def; sleep 1; echo ghi) | Node Consumption0.js <Buffer 0a = "" 61 = "" 62 = "" 63 = ""> <buffer 0a = "" 64 = "" 65 = "" 66 = ""> <buffer 0a = "" 67 = "" 68 = "" 69 = ""> null </ buffer> </ / tampon> </ / buffer> </ code>Lorsqu'il y a des données à lire dans le flux lisible, le flux déclenchera l'événement «lisible», afin que la méthode .read () puisse être appelée pour lire les données pertinentes. Lorsqu'il n'y a pas de données à lire dans le flux lisible, .read () renvoie Null, afin que l'appel de .read () puisse être terminé et attendre que l'événement «lisible» suivant soit déclenché. Voici un exemple d'utilisation .read (n) pour lire 3 octets à chaque fois à partir de l'entrée standard:
<code> process.stdin.on ('lisible', function () {var buf = process.stdin.read (3); console.dir (buf);}); </code>L'exécution du programme comme suit montre que les résultats de sortie ne sont pas terminés!
<code> $ (Echo ABC; Sleep 1; Echo Def; Sleep 1; Echo Ghi) | Consommation de nœud1.js <tampon 61 = "" 62 = "" 63 = ""> <buffer 0a = "" 64 = "" 65 = ""> <buffer 0a = "" 66 = "" 67 = ""> </ buffer> </ buffer> </ tamper> </code>
Cela devrait être fait pour que les données supplémentaires soient laissées dans le tampon interne du flux, et nous devons informer le flux que nous voulons lire plus de données. lire (0) peut y parvenir.
<code> process.stdin.on ('lisible', function () {var buf = process.stdin.read (3); console.dir (buf); process.stdin.read (0);}); </code>Les résultats de cette course sont les suivants:
<code> $ (Echo ABC; Sleep 1; Echo Def; Sleep 1; Echo Ghi) | Node Consomption2.js <Buffer 0a = "" 64 = "" 65 = ""> <Buffer 0a = "" 68 = "" 69 = ""> </ buffer> </ buffer> </code>
Nous pouvons utiliser .unShift () pour redimensionner les données à la tête de la file d'attente de données en streaming, afin que nous puissions continuer à lire les données marquées. Comme dans le code suivant, le contenu d'entrée standard sera sorti par ligne:
<code> var offset = 0; process.stdin.on ('lisible', function () {var buf = process.stdin.read (); if (! buf) return; for (; offset <buf.length; offset ++) {if (buf [offset] === 0x0a) {console.dir (buf.slice (0, offSet). buf.slice (offset + 1); offset = 0; process.stdin.unshift (buf); return;}} process.stdin.unshift (buf);}); $ tail -n +50000 / usr / share / dict / américain-English | Tête -N10 | Node Lines.js 'Hearries'''heartties'''heAartly''heartanss''heartanss'''heart’''heartness /' S''heartland''heartland / 'S''heartlands''heartless''heartless' </code>Bien sûr, il existe de nombreux modules qui peuvent implémenter cette fonction, comme Split.
3-3. Streams en écriture
Les flux écrivables ne peuvent être utilisés que comme paramètre de destination de la fonction .pipe (). Le code suivant:
<code> src.pipe (writeableStream); </code>
1> Créer un flux écrivative
Réécrivez la méthode ._write (Chunk, ENC, Next) pour accepter les données d'un flux lisible.
<code> var witteable = require ('stream'). witchable; var ws = writeable (); ws._write = function (chunk, enc, next) {console.dir (chunk); next ();}; process.stdin.pipe (ws); $ (echo beep; sleep 1; echo boop) | node write0.js <tampon 0a = "" 62 = "" 65 = "" 70 = ""> <tampon 0a = "" 62 = "" 6f = "" 70 = ""> </ tampon> </ buffer> </code>Le premier morceau de paramètre est les données rédigées par le saisissant de données. La deuxième fin du paramètre est le format de codage des données. Le troisième paramètre Next (ERR) informe l'écrivain de données via la fonction de rappel que plus de temps peut être écrit. Si le flux lisible écrit une chaîne, la chaîne sera convertie en tampon par défaut. Si le paramètre écrivable ({Decodestrings: False}) est défini lors de la création du flux, la conversion ne sera pas effectuée. Si les données sont rédigées par le flux lisible, vous devez créer un flux écrivative de cette manière
<code> writeable ({objectMode: true}) </code>2> Écrivez des données au flux écrivative
Appelez la méthode .Write (Data) de flux écrivative pour terminer la rédaction de données.
<code> process.stout.write ('bip boop / n'); </code>L'appel de la méthode .end () avertit le flux scénariste que les données ont été rédigées pour terminer.
<code> var fs = require ('fs'); var ws = fs.createwRiteStream ('message.txt'); ws.write ('bip'); setTimeout (function () {ws.end ('boop / n');}, 1000); $ node writing1.js $ cat message.txtbeep boop </code>Si vous avez besoin de définir la taille du tampon du flux écrit, lors de la création du flux, vous devez définir Opts.HighwaterMark, de sorte que si les données du tampon dépassent Opts.Highwatermark, la méthode .write (Data) sera renvert faux. Lorsque le tampon est écrit, le flux en écriture déclenchera l'événement «drain».
3-4. Streams classiques
Classic Streams est une interface plus ancienne, qui est apparue pour la première fois dans la version Node 0.4, mais il est toujours très bon de comprendre son principe de fonctionnement.
où. Lorsqu'un flux est enregistré avec l'événement "Data" à la fonction, le flux fonctionnera en mode ancienne version, c'est-à-dire que l'ancienne API sera utilisée.
1> Streams lisibles classiques
L'événement classique lisible des flux est un déclencheur d'événements. Si des flux lisibles classiques ont des données à lire, il déclenche l'événement "Data". Lorsque les données sont lues, l'événement "fin" sera déclenché. La méthode .Pipe () détermine si le flux a des données à lire en vérifiant la valeur de Stream. Voici un exemple d'impression des lettres AJ à l'aide de flux lisibles classiques:
<code> var stream = require ('stream'); var stream = new Stream; stream.readable = true; var c = 64; var iv = setInterval (function () {if (++ c> = 75) {ClearInterval (iv); stream.emit ('end');} else stream.emit ('data', string.fromcharcode (c));}, 100); Stream.Pipe (process.stout); $ node classic0.jsabcdefghij </code>Si vous souhaitez lire les données du flux lisible et lisible, enregistrez les fonctions de rappel des deux événements "Data" et "End" Events, le code est le suivant:
<code> process.stdin.on ('data', fonction (buf) {console.log (buf);}); process.stdin.on ('end', function () {console.log ('__ end __');}); $ (echo beep; sleep 1; echo boop) | node classic1.js <buffer 0a = "" 62 = "" 65 = "" 70 = ""> <tampon 0a = "" 62 = "" 6f = "" 70 = ""> __ end __ </ buffer> </ buffer> </code>Il convient de noter que si vous utilisez cette méthode pour lire les données, vous perdrez les avantages de l'utilisation de la nouvelle interface. Par exemple, lorsque vous écrivez des données sur un flux avec une latence très élevée, vous devez prêter attention à l'équilibre entre la lecture et l'écriture de données, sinon cela entraînera une grande quantité de données en cache en mémoire, ce qui entraînera un gaspillage de beaucoup de mémoire. Généralement, il est fortement recommandé d'utiliser la méthode .pipe () du flux, afin que vous n'ayez pas à écouter les événements «données» et «fin» vous-même, et vous n'avez pas à vous soucier du problème de la lecture et de l'écriture déséquilibrées. Bien sûr, vous pouvez également utiliser au lieu d'écouter vous-même les événements "Data" et "Fin", tels que le code suivant:
<code> var via = require ('via'); process.stdin.pipe (via (write, end)); function write (buf) {console.log (buf);} function end () {console.log ('__ end __');} $ (echo beep; sleep 1; echo boop) | Node via.js <tampon 0a = "" 62 = "" 65 = "" 70 = ""> <tampon 0a = "" 62 = "" 6f = "" 70 = ""> __ end __ </ buffer> </ buffer> </code>Ou vous pouvez également utiliser la circulation concat pour cache le contenu de l'ensemble du flux:
<code> var concat = require ('concat-stream'); process.stdin.pipe (Concat (fonction (body) {console.log (json.parse (body));})); $ echo '{"bip": "boop"}' | node concat.js {bip: 'boop'} </code>Bien sûr, si vous devez écouter vous-même les événements "Data" et "End", vous pouvez utiliser la méthode .pause () pour suspendre des flux lisibles classiques et continuer à déclencher l'événement "Data" lorsque le flux de données d'écriture n'est pas écrivative. Attendez que les données d'écriture de flux soient inscrites avant d'utiliser la méthode .Resume () informe le flux pour continuer à déclencher l'événement "Data" pour continuer à lire.
données.
2> Streams écrits classiques
Les flux d'écriture classiques sont très simples. Il n'y a que trois méthodes: .write (buf), .end (buf) et .destroy (). Le paramètre BUF de la méthode .end (buf) est facultatif. Si ce paramètre est sélectionné, il équivaut à Stream.Write (BUF); Stream.end () Opération. Il convient de noter que lorsque le tampon du flux est plein, c'est-à-dire que le flux ne peut pas être écrit. La méthode d'écriture (buf) renvoie false. Si le flux est à nouveau inscrit, le flux déclenchera l'événement de drainage.
4. Transformer
La transformation est un flux qui filtre la sortie des données de lecture.
5. Duplex
Duplex Stream est un flux bidirectionnel qui peut être lisible ou écrit. Par exemple, un ci-dessous est un flux duplex:
<code> a.pipe (b) .Pipe (a) </code>
Le contenu ci-dessus est le manuel d'utilisation du flux de données de flux NodeJS qui vous a été introduit par l'éditeur. J'espère que cela vous sera utile!