1. Öffnungsanalyse
Stream ist eine abstrakte Schnittstelle, die von vielen Objekten im Knoten implementiert wird. Beispielsweise ist eine Anforderung an einen HTTP -Server ein Stream, und STDOut ist ebenfalls ein Stream. Streams sind lesbar, beschreibbar oder beides.
Der erste Kontakt mit Stream begann mit der frühen Unix. Jahrzehnte der Praxis haben bewiesen, dass die Idee von Stream einfach einige riesige Systeme entwickeln kann.
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.
Eine TCP -Verbindung ist sowohl ein lesbarer Stream als auch ein beschreibbarer Stream, während eine HTTP -Verbindung unterschiedlich ist. Ein HTTP -Anforderungsobjekt ist ein lesbarer Stream, während ein HTTP -Antwortobjekt ein beschreibbarer Stream ist.
Der Stream -Übertragungsprozess wird standardmäßig in Form eines Puffers übertragen, es sei denn, Sie setzen andere Codierungsformulare dafür, ist Folgendes ein Beispiel:
Die Codekopie lautet wie folgt:
var http = required ('http');
var server = http.createServer (Funktion (req, res) {
Res.WriteHeader (200, {'Inhalts-Typ': 'Text/Plain'});
res.end ("Hallo, Big Bear!");
});
Server.Listen (8888);
console.log ("HTTP -Server, der auf Port 8888 ausgeführt wird ...");
Nach dem Laufen erscheint der verstümmelte Code, da der angegebene Zeichensatz nicht festgelegt ist, wie z. B. "UTF-8".
Ändern Sie es einfach:
Die Codekopie lautet wie folgt:
var http = required ('http');
var server = http.createServer (Funktion (req, res) {
Res.WriteHeader (200, {
'Inhaltstyp': 'text/plain; charset = utf-8' // fügen Sie charset = utf-8 hinzu
});
res.end ("Hallo, Big Bear!");
});
Server.Listen (8888);
console.log ("HTTP -Server, der auf Port 8888 ausgeführt wird ...");
Auslaufergebnisse:
Warum Stream verwenden?
Die E/A im Knoten ist asynchron. Das Lesen und Schreiben auf Scheibe und Netzwerk erfordert daher das Lesen und Lesen von Daten über Rückruffunktionen. Das Folgende ist ein Beispiel für einen Datei -Download
Auf Code:
Die Codekopie lautet wie folgt:
var http = required ('http');
var fs = fordert ('fs');
var server = http.createServer (Funktion (req, res) {
fs
res.end (Daten);
});
});
Server.Listen (8888);
Der Code kann die erforderlichen Funktionen implementieren, aber der Dienst muss die gesamten Dateidaten vor dem Senden der Dateidaten unterspeichern. Wenn die Datei "Data.txt" sehr ist
Wenn es groß ist und eine große Parallelität aufweist, wird viel Gedächtnis verschwendet. Da der Benutzer warten muss, bis die gesamte Datei in den Speicher entfernt ist, um die Dateidaten zu akzeptieren, führt dies zu
Die Benutzererfahrung ist ziemlich schlecht. Glücklicherweise sind beide Parameter (Req, Res) Stream, sodass wir fs.createadstream () anstelle von fs.readFile () verwenden können. wie folgt:
Die Codekopie lautet wie folgt:
var http = required ('http');
var fs = fordert ('fs');
var server = http.createServer (Funktion (req, res) {
var stream = fs.createadstream (__ Dirname + '/data.txt');
stream.pipe (res);
});
Server.Listen (8888);
Die Methode .pipe () hört auf die Ereignisse "Daten" und "End" von fs.createadstream (), so dass die Datei "Data.txt" nicht zwischengespeichert werden muss.
Eine Datei kann sofort nach Abschluss der Clientverbindung an den Client gesendet werden. Ein weiterer Vorteil der Verwendung von .pipe () besteht darin, dass es bei einem Kunden gelöst werden kann
Lesen und schreiben Sie ein Ungleichgewicht, das durch sehr große Endverzögerung verursacht wird.
Es gibt fünf grundlegende Streams: lesbar, beschreibbar, Transformation, Duplex und "Klassiker". (Bitte überprüfen Sie die API für Details)
2. Beispiele einführen
Wenn die Daten, die verarbeitet werden müssen, nicht gleichzeitig im Speicher geladen werden können oder wenn die Verarbeitung beim Lesen effizienter ist, müssen wir Datenströme verwenden. NodeJS bietet Operationen in Datenströmen über verschiedene Streams.
Wenn Sie das Programm mit großem Dateikopie als Beispiel nehmen, können wir einen schreibgeschützten Datenstrom für die Datenquelle erstellen. Das Beispiel lautet wie folgt:
Die Codekopie lautet wie folgt:
var rs = fs.createadstream (Pathname);
rs.on ('Daten', Funktion (Chunk) {
Dosen (Chunk); // Verwenden Sie die spezifischen Details, wie Sie möchten
});
rs.on ('end', function () {
Reinigung ();
});
Datenereignisse im Code werden kontinuierlich ausgelöst, unabhängig davon, ob die Dosenfunktion verarbeitet werden kann. Der Code kann wie folgt geändert werden, um dieses Problem zu lösen.
Die Codekopie lautet wie folgt:
var rs = fs.createadstream (SRC);
rs.on ('Daten', Funktion (Chunk) {
rs.pause ();
DOSOMET (Chunk, function () {
Rs.Resume ();
});
});
rs.on ('end', function () {
Reinigung ();
});
Der Dosenfunktion wird ein Rückruf hinzugefügt, sodass wir vor dem Verarbeitung von Daten die Daten lesen und die Daten nach der Verarbeitung von Daten weiter lesen können.
Darüber hinaus können wir wie folgt einen nur Schreibdatenstrom für das Datenziel erstellen:
Die Codekopie lautet wie folgt:
var rs = fs.createadstream (SRC);
var ws = fs.createwritestream (DST);
rs.on ('Daten', Funktion (Chunk) {
Ws.Write (Chunk);
});
rs.on ('end', function () {
Ws.end ();
});
Nachdem Dosen durch das Schreiben von Daten in den Schreibstream ersetzt wurde, sieht der obige Code wie ein Dateikopierprogramm aus. Der obige Code hat jedoch die oben genannten Probleme. Wenn die Schreibgeschwindigkeit nicht mit der Lesegeschwindigkeit Schritt halten kann, explodiert das Schreiben des Cache nur im Datenstrom. Wir können beurteilen, ob die eingehenden Daten an das Ziel geschrieben oder vorübergehend in den Cache gesetzt wurden, basierend auf dem Rückgabewert der .Write -Methode, und zu Richter, wenn nur die Schreibdaten an dem Ziel auf der Grundlage des Drain -Ereignisses geschrieben wurden, und die nächsten Daten übergeben werden. Daher ist der Code wie folgt:
Die Codekopie lautet wie folgt:
var rs = fs.createadstream (SRC);
var ws = fs.createwritestream (DST);
rs.on ('Daten', Funktion (Chunk) {
if (ws.write (chunk) === false) {
rs.pause ();
}
});
rs.on ('end', function () {
Ws.end ();
});
Ws.on ('Drain', function () {
Rs.Resume ();
});
Schließlich wird die Datenübertragung vom schreibgeschützten Datenstrom zum Schreibdatenstrom realisiert, und die explosionssichere Lagersteuerung ist enthalten. Da es viele Nutzungsszenarien gibt, wie das obige große Dateikopierprogramm, liefert NodeJs direkt die .pipe -Methode, um dies zu tun, und seine interne Implementierungsmethode ähnelt dem obigen Code.
Hier finden Sie einen umfassenderen Prozess des Kopierens von Dateien:
Die Codekopie lautet wie folgt:
var fs = erfordern ('fs'),
Pfad = erfordern ('Pfad'),
out = process.stdout;
var filepath = '/bb/bigbear.mkv';
var readStream = fs.createadStream (filepath);
var writeStream = fs.createwritestream ('file.mkv');
var stat = fs.statync (filepath);
var sumalSize = stat.size;
var condsLength = 0;
var lastsize = 0;
var startTime = date.now ();
ReadStream.on ('Data', Funktion (Chunk) {
condentLength += chunk.length;
if (WriteStream.write (Chunk) === false) {
readStream.pause ();
}
});
readStream.on ('end', function () {
WriteStream.end ();
});
WriteStream.on ('Drain', function () {
ReadStream.Resume ();
});
setTimeout (Funktionshow () {
var prozent = math.ceil (((bestandene Länge / Gesamtgröße) * 100);
var size = math.ceil (bestandene Länge / 1000000);
var diff = Größe - LastSize;
LastSize = Größe;
out.clearline ();
out.cursorto (0);
out.write ('abgeschlossen' + size + 'mb,' + Prozent + '%, Geschwindigkeit:' + diff * 2 + 'mb/s');
if (condentLength <sotalSize) {
setTimeout (Show, 500);
} anders {
var endime = date.now ();
console.log ();
console.log ('wenn geteilt:' + (Endime - StartTime) / 1000 + 'Sekunden.');
}
}, 500);
Sie können den obigen Code als "Copy.js" speichern. Experiment: Wir haben einen rekursiven SetTimeout hinzugefügt (oder setInterval direkt verwenden), um ein Zuschauer zu sein.
Beobachten Sie alle 500 ms den Abschlussschritt und schreiben Sie die vollständige Größe, den Prozentsatz und die Kopiergeschwindigkeit zusammen in die Konsole. Beim Kopieren wird die Gesamtzeit berechnet.
Drei, lasst uns zusammenfassen
(1) das Konzept des Streams verstehen.
(2) die Verwendung relevanter Stream -API
(3) Achten Sie auf die Kontrolle von Details, z. B.: Kopieren großer Dateien, mithilfe der Form von "Chunk -Daten" zum Sharding.
(4) die Verwendung von Rohr
(5) betonen erneut ein Konzept: Eine TCP -Verbindung ist sowohl ein lesbarer Stream als auch ein beschreibbarer Stream, während eine HTTP -Verbindung unterschiedlich ist. Ein HTTP -Anforderungsobjekt ist ein lesbarer Stream, während ein HTTP -Antwortobjekt ein beschreibbarer Stream ist.