1. Einführung
In diesem Artikel werden die grundlegenden Methoden zur Entwicklung von Programmen mithilfe von Node.js -Streams eingeführt.
<Code> "Wir sollten einige Möglichkeiten haben, Programme wie Garden Hose-Screw Inanother-Segment zu verbinden, wenn es notwendig wird, Daten in einem weiteren Weg zu massieren. Dies ist auch der Weg von IO." Doug McIlroy. 11. Oktober 1964 </code>
Der erste, der mit Stream in Kontakt kam, war die Jahrzehnte der Praxis, die mit der frühen Unix begann, was bewies, dass Stream -Ideen einfach einige riesige Systeme entwickeln können. In Unix wird Stream durch | implementiert; Im Knoten werden als integriertes Stream-Modul viele Kernmodule und Drei-Parteien-Module verwendet. Wie bei UNIX ist der Hauptbetrieb des Knotenstreams .Pipe () und Benutzer können den Gegendruckmechanismus verwenden, um das Gleichgewicht zwischen Lesen und Schreiben zu steuern.
Stream kann Entwicklern eine einheitliche Schnittstelle zur Verfügung stellen, die das Lese- und Schreibbalance zwischen Streams durch abstrakte Stream -Schnittstellen wiederverwenden und steuern kann.
2. Warum Stream verwenden
Die E/A im Knoten ist asynchron. Das Lesen und Schreiben auf Scheibe und Netzwerk muss daher über Rückruffunktionen gelesen und schreiben. Das Folgende ist ein einfacher Code für einen Datei -Download -Server:
<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);});});server.listen(8000);</code>Diese Codes können die erforderlichen Funktionen implementieren, der Dienst muss jedoch die gesamten Dateidaten vor dem Senden der Dateidaten zwischenspeichern. Wenn die Datei "Data.txt" groß ist und die Zuschauer groß ist, wird viel Speicher verschwendet. Da der Benutzer warten muss, bis die gesamte Datei vor dem Speicher zwischen den Dateidaten zwischengespeichert wird, führt dies zu einer sehr schlechten Benutzererfahrung. Glücklicherweise sind beide Parameter (Req, Res) Stream, sodass wir fs.createadstream () anstelle von fs.readFile () verwenden können:
<Code> var http = required ('http'); var fs = required ('fs'); var server = http.createServer (function (req, res) {var stream = fs.createReadstream (__ DirName + '/data.txt');Die Methode .pipe () hört auf die Ereignisse "Daten" und "End" von fs.createadstream (), so dass die Datei "Data.txt" die gesamte Datei nicht zwischenspeichern muss. Nach Abschluss der Clientverbindung kann sofort ein Datenblock an den Client gesendet werden. Ein weiterer Vorteil der Verwendung von .pipe () besteht darin, dass das Problem des Lesens und Schreibens von Ungleichgewicht durch sehr große Kundenlatenz lösen kann. Wenn Sie die Datei komprimieren und senden möchten, können Sie ein Drei-Parteien-Modul verwenden, um sie zu implementieren:
<Code> var http = required ('http'); var fs = required ('fs'); var reastpressor = required ('reppressor'); var server = http.createServer (function (req, res) {var stream = fs.CreateReadStream (__ Dirname + '/data.txt'); stream.pipe (tastpressor (req)). pipe (res);}); server.listen (8000); </code>Auf diese Weise komprimiert die Datei den Browser, der GZIP und Deflate unterstützt. Das Unterdrückungsmodul behandelt alle Inhaltskodierung.
Stream macht die Entwicklung von Programmen einfach.
3. Grundkonzepte
Es gibt fünf grundlegende Streams: lesbar, beschreibbar, Transformation, Duplex und „Klassiker“.
3-1, Pfeife
Alle Arten von Stream verwenden .Pipe (), um ein Eingangs- und Ausgabepaar zu erstellen, ein lesbares Stream -SRC zu erhalten und seine Daten wie folgt an die beschreibbare Stream -DST auszugeben:
<Code> src.pipe (dst) </code>
Die .pipe (DST) -Methode gibt den DST -Stream zurück, so dass mehrere .pipe () nacheinander wie folgt verwendet werden können:
<code> a.pipe (b) .pipe (c) .pipe (d) </code>
Die Funktion entspricht dem folgenden Code:
<code> a.pipe (b); b.pipe (c); C.Pipe (d); </code>
3-2. Lesbare Streams
Durch Aufrufen der Methode .pipe () für lesbare Streams können Sie die Daten lesbarer Streams in einen beschreibbaren, transformierenden oder Duplex -Stream schreiben.
<Code> readableStream.pipe (dst) </code>
1> Erstellen Sie einen lesbaren Stream
Hier erstellen wir einen lesbaren Stream!
<Code> var lesable = Request ('Stream'). lesbar; var rs = new Readable; rs.push ('peep');Rs.Push (NULL) benachrichtigt den Datenempfänger, dass die Daten gesendet wurden.
Beachten Sie, dass wir Rs.Pipe (Process.stdout) nicht bezeichnet haben; Bevor Sie alle Daten in den lesbaren Stream drücken, alle Daten in den lesbaren Stream werden jedoch weiterhin vollständig ausgegeben, da der lesbare Stream alle gedrückten Daten vor dem Reading der Daten vorsieht. In vielen Fällen besteht ein besserer Weg darin, die Daten nur in den lesbaren Stream zu drücken, wenn die Daten die angeforderten Daten empfangen, anstatt die gesamten Daten zu speichern. Schreiben wir die Funktion ._read () unten neu:
<Code> var lesable = required ('stream'). lesbar; var rs = lesable (); var c = 97; rs._read = function () {rs.push (String.FromChode (c ++)); if (c> 'z'chodeat (0)) rs.push (null); read1.jsabcdefghijklmnopqrstuvwxyz </code>Der obige Code implementiert das Umschreiben der Methode _read (), um Daten nur dann in den lesbaren Stream zu drücken, wenn der Datenempfänger die Daten anfordert. Die Methode _Read () kann auch einen Größenparameter empfangen, der die angeforderte Datengröße der Daten angibt. Der lesbare Stream kann diesen Parameter bei Bedarf ignorieren.
Beachten Sie, dass wir auch util.inherits () verwenden können, um lesbare Streams zu erben. Um zu veranschaulichen, dass die Methode _read () nur aufgerufen wird, wenn der Datenempfänger Daten anfordert, führen wir wie folgt eine Verzögerung vor, wenn Daten in den lesbaren Stream drücken:
<Code> var lesable = required ('stream'). lesbar; var rs = lesable (); var c = 97 - 1; rs._read = function () {if (c> = 'z'.charcodeat (0)) return rs.push (null); 100);};Beim Ausführen des Programms mit dem folgenden Befehl stellten wir fest, dass die Methode _Read () nur 5 Mal bezeichnet wurde:
<Code> $ node read2.js | head -c5abcde_read () bezeichnet 5 -mal </code>
Der Grund für die Verwendung eines Timers ist, dass das System Zeit braucht, um Signale zu senden, um das Programm zur Schließung der Pipeline zu informieren. Verwenden Sie Process.stdout.on ('Fehler', FN), um das System zu verarbeiten, das ein Sigpipe -Signal sendet, da der Header -Befehl die Pipeline schließt, da dies den Prozess verursacht. Wenn Sie einen lesbaren Stream erstellen möchten, der in eine beliebige Form von Daten gedrückt werden kann, legen Sie einfach den Parameter -Objektmodus auf True ein, wenn Sie den Stream erstellen, z. B. lesbar ({ObjectMode: true}).
2> lesbare Stream -Daten
In den meisten Fällen verwenden wir einfach die Pipe -Methode, um die Daten des lesbaren Streams in eine andere Form des Streams umzuleiten. In einigen Fällen kann es jedoch nützlicher sein, Daten direkt aus dem lesbaren Stream zu lesen. wie folgt:
<Code> process.stdin.on ('lesable', function () {var buf = process.stdin.read (); console.dir (buf);}); Knotenkonsum0.js <Buffer 0a = "" 61 = "" 62 = "" 63 = ""> <Buffer 0a = "" 64 = "" 65 = "" 66 = ""> <buffer 0a = "" 67 = "" 68 = "" 69 = ""Wenn im lesbaren Stream Daten gelesen werden, löst der Stream das "lesbare" Ereignis aus, sodass die Methode .read () aufgerufen werden kann, um die relevanten Daten zu lesen. Wenn im lesbaren Stream keine Daten gelesen werden müssen, wird .Read () null zurückgegeben, damit der Ruf von .read () beendet werden kann und auf das nächste "lesbare" Ereignis ausgelöst wird. Hier ist ein Beispiel für die Verwendung von .read (n), um 3 Bytes jedes Mal aus Standardeingabe zu lesen:
<Code> process.stdin.on ('lesable', function () {var buf = process.stdin.read (3); console.dir (buf);}); </code>Das Ausführen des Programms wie folgt zeigt, dass die Ausgabergebnisse nicht abgeschlossen sind!
<Code> $ (Echo ABC; Schlaf 1; Echo def; Schlaf 1; Echo Ghi) | Knotenkonsum1.js <Buffer 61 = "" 62 = "" 63 = ""> <Buffer 0a = "" 64 = "" 65 = ""> <Buffer 0a = "" 66 = "" 67 = ""> </buffer> </buffer> </buffer> </code>
Dies sollte für die zusätzlichen Daten erfolgen, die im internen Puffer des Streams gelassen werden, und wir müssen den Stream, den wir mehr Daten lesen möchten, benachrichtigen. Lesen (0) kann dies erreichen.
<code> process.stdin.on ('lesable', function () {var buf = process.stdin.read (3); console.dir (buf); process.stdin.read (0);}); </code>Die Ergebnisse dieses Laufs sind wie folgt:
<Code> $ (Echo ABC; Schlaf 1; Echo def; Schlaf 1; Echo Ghi) | Knoten Consumpt2.js <Buffer 0a = "" 64 = "" 65 = ""> <Buffer 0a = "" 68 = "" 69 = ""> </buffer> </buffer> </code>
Wir können .unshift () verwenden, um die Daten zurück zum Kopf der Streaming -Datenwarteschlange zu ändern, damit wir die festgelegten Daten weiter lesen können. Wie im folgenden Code wird der Standardeingangsinhalt nach Zeile ausgegeben:
<Code> var offset = 0; process.stdin.on ('lesable', function () {var buf = process.stdin.read (); if (! buf) return; für (; offset <buf.length; offset ++) {if (buf [offset] === 0x0a) {console.dir (buf.sl (buf.slice)). buf.slice (offset + 1); offset = 0; process.stdin.unshift (buf); return;}} prozess.stdin.unshift (buf);}); Kopf -n10 | node lines.js 'Hearties''Hearties''Heartily''heartess''heartess'Heartiness''Heartiness/' s''Heartland''heartland/'s'Heartlands''Heartless'Hearteless' </code>Natürlich gibt es viele Module, die diese Funktion implementieren können, z. B. Split.
3-3. Beschreibbare Streams
Beschreibbare Streams können nur als Zielparameter der Funktion .pipe () verwendet werden. Der folgende Code:
<Code> src.pipe (WritAbleStream); </code>
1> Erstellen Sie einen beschreibbaren Stream
Schreiben Sie die Methode ._Write (Chunk, ENC, Nächste) um, um Daten aus einem lesbaren Stream zu akzeptieren.
<Code> var wrapable = required ('stream'). beschreibbar; var ws = writable (); Knoten write0.js <buffer 0a = "" 62 = "" 65 = "" 70 = ""> <buffer 0a = "" 62 = "" 6f = "" 70 = ""> </buffer> </buffer> </code>Der erste Parameter -Chunk sind die Daten, die vom Dateneingang verfasst wurden. Das zweite Parameterende ist das Codierungsformat der Daten. Der dritte Parameter als nächstes benachrichtigt den Datenschreiber über die Rückruffunktion, dass mehr Zeit geschrieben werden kann. Wenn der lesbare Stream eine Zeichenfolge schreibt, wird die Zeichenfolge standardmäßig in einen Puffer konvertiert. Wenn beim Erstellen des Streams der Parameter für schriftliche ({decodestrings: false}) festgelegt wird, wird die Konvertierung nicht durchgeführt. Wenn die Daten vom lesbaren Stream geschrieben werden, müssen Sie auf diese Weise einen beschreibbaren Stream erstellen
<code> schriftlich ({ObjectMode: true}) </code>2> Daten in beschreibbarer Stream schreiben
Rufen Sie die Methode .Write (Daten) des beschreibbaren Streams an, um das Schreiben von Daten zu vervollständigen.
<Code> process.stdout.write ('peep boop/n'); </code>Aufrufen der Methode .end () benachrichtigt den beschreibbaren Stream, dass die Daten geschrieben wurden.
<Code> var fs = required ('fs'); var ws = fs.createwritestream ('message.txt');Wenn Sie die Größe des Puffers des beschreibbaren Streams festlegen müssen, müssen Sie beim Erstellen des Streams opts.highwatermark festlegen. Wenn die Daten im Puffer OPTS.HighWatermark überschreiten, gibt die Methode .Write (Daten) FALSE zurück. Wenn der Puffer beschreibbar ist, löst der beschreibbare Stream das Ereignis "Drain" aus.
3-4. Klassische Streams
Classic Streams ist eine ältere Schnittstelle, die erstmals in der Node 0.4 -Version erschien, aber es ist immer noch sehr gut, ihr Betriebsprinzip zu verstehen.
Wo. Wenn ein Stream mit dem Ereignis "Daten" zurück in die Funktion registriert ist, funktioniert der Stream im alten Versionsmodus, dh die alte API wird verwendet.
1> Klassische lesbare Streams
Das klassische lesbare Streams -Ereignis ist ein Ereignisauslöser. Wenn klassische lesbare Streams zu lesen sind, löst dies das "Daten" -Ereignis aus. Wenn die Daten gelesen werden, wird das "End" -Ereignis ausgelöst. Die Methode .pipe () bestimmt, ob der Stream Daten gelesen hat, indem der Wert von Stream.readable überprüft wird. Hier ist ein Beispiel für das Drucken von AJ -Buchstaben mit klassischen lesbaren Streams:
<code> var stream = required ('stream'); var stream = new Stream; Stream.readable = true; var c = 64; var iv = setInterval (function () {if (++ c> = 75) {clearInterval (iv); Stream.emit ('end'); 100); Stream.Pipe (process.stdout);Wenn Sie Daten aus dem klassischen lesbaren Stream lesen möchten, registrieren Sie die Callback -Funktionen der beiden Ereignisse "Daten" und "End" -Ereignisse, der Code lautet wie folgt:
<Code> process.stdin.on ('data', function (buf) {console.log (buf);}); process.stdin.on ('end', function () {console.log ('__ end __');}); Node Classic1.js <Buffer 0a = "" 62 = "" 65 = "" 70 = ""> <Buffer 0a = "" 62 = "" 6f = "" 70 = ""> __ Ende __ </buffer> </buffer> </code>Es ist zu beachten, dass Sie, wenn Sie diese Methode zum Lesen von Daten verwenden, die Vorteile der Verwendung der neuen Schnittstelle verlieren. Wenn Sie beispielsweise Daten mit einer sehr hohen Latenz in einen Stream schreiben, müssen Sie auf das Gleichgewicht zwischen Lese- und Schreibdaten achten. Andernfalls wird eine große Menge an Daten in Speicher abgeschnitten, was zu einer Menge Speicherverschwendung führt. Im Allgemeinen wird dringend empfohlen, die .pipe () -Methode des Streams zu verwenden, sodass Sie die Ereignisse "Daten" und "End" selbst nicht anhören müssen, und Sie müssen sich keine Sorgen um das Problem des unausgeglichenen Lesens und Schreibens machen. Natürlich können Sie auch verwenden, anstatt sich die "Daten" und "Ende" selbst zuzuhören, z. B. den folgenden Code:
<Code> var über = required ('bis'); process.stdin.pipe (bis (schriftlich, end)); Knoten durch.Oder Sie können auch Concat-Stream verwenden, um den Inhalt des gesamten Streams zwischenzuspeichern:
<Code> var concat = required ('concat-stream'); process.stdin.pipe (consol (function (body) {console.log (json.parse (Körper));})); $ echo '{"Beep": "Boop"}' | Knotenconcat.js {Beep: 'Boop'} </code>Wenn Sie sich die Ereignisse "Daten" und "Ende" selbst anhören müssen, können Sie natürlich die Methode .Pause () verwenden, um klassische lesbare Streams zu pausieren und weiterhin das "Daten" -Ereignis auszulösen, wenn der Strom der Schreibdaten nicht beschreibbar ist. Warten Sie, bis das Schreiben von Stream -Schreibdaten schriftlich ist, bevor die Methode .Resume () den Stream benachrichtigt, um das "Daten" -Ereignis weiter auszulösen, um dies weiter zu lesen.
Daten.
2> klassische beschreibbare Streams
Klassische beschreibbare Streams sind sehr einfach. Es gibt nur drei Methoden: .Write (BUF), .end (BUF) und .Destroy (). Der BUF -Parameter der Methode .end (BUF) ist optional. Wenn dieser Parameter ausgewählt ist, entspricht er stream.write (buf); Stream.end () Betrieb. Es ist zu beachten, dass der Stream, wenn der Puffer des Streams voll ist, nicht geschrieben werden kann. Die Write (BUF) -Methode gibt FALSE zurück. Wenn der Stream erneut geschrieben ist, löst der Stream das Drain -Ereignis aus.
4. Transformation
Transformation ist ein Stream, der die Ausgabe von Lesedaten filtert.
5. Duplex
Duplex-Stream ist ein Zwei-Wege-Stream, der lesbar oder geschrieben werden kann. Beispielsweise ist ein unten stehender Duplex -Stream:
<Code> a.pipe (b) .pipe (a) </code>
Der obige Inhalt ist das vom Editor vorgelegte NodeJS -Stream -Datenflussbenutzerhandbuch. Ich hoffe, es wird für Sie hilfreich sein!