Heim > Web-Frontend > js-Tutorial > Hauptteil

Eine eingehende Analyse von Stream in Node

青灯夜游
Freigeben: 2023-01-29 19:46:30
nach vorne
2828 Leute haben es durchsucht

Was ist Flow? Wie versteht man den Fluss? Der folgende Artikel wird Ihnen ein tiefgreifendes Verständnis von Streams in Nodejs vermitteln. Ich hoffe, er wird Ihnen hilfreich sein!

Eine eingehende Analyse von Stream in Node

Stream ist eine abstrakte Datenschnittstelle, die EventEmitter erbt. Sie kann Daten senden/empfangen, wie unten gezeigt: Eine eingehende Analyse von Stream in Node

Stream ist kein einzigartiges Konzept in Node, sondern ein Betriebssystem . Die grundlegendste Betriebsmethode in Linux |. ist Stream, aber sie ist auf Knotenebene gekapselt und stellt die entsprechende API bereit

Warum müssen wir es Stück für Stück tun?

Verwenden Sie zunächst den folgenden Code, um eine Datei mit einer Größe von ca. 400 MB zu erstellen [Empfehlung für entsprechende Tutorials: nodejs-Video-Tutorial]

Untitled 1.png

Wenn wir readFile zum Lesen verwenden, ist der folgende Code

Untitled 2.png

normal. Beim Start Der Dienst belegt etwa 10 MB Speicher.

Untitled 3.png

Wenn Sie curl http://127.0.0.1:8000 verwenden, um eine Anfrage zu initiieren, beträgt der Speicher etwa 420 MB, was ungefähr dem gleichen Wert entspricht Größe wie die von uns erstellte Datei. lazy"/>curl http://127.0.0.1:8000发起请求时,内存变为了 420MB 左右,和我们创建的文件大小差不多

Untitled 4.png

改为使用使用 stream 的写法,代码如下

Untitled 5.png

再次发起请求时,发现内存只占用了 35MB 左右,相比 readFile 大幅减少

Untitled 6.png

如果我们不采用流的模式,等待大文件加载完成在操作,会有如下的问题:

  • 内存暂用过多,导致系统崩溃
  • CPU 运算速度有限制,且服务于多个程序,大文件加载过大且时间久

总结来说就是,一次性读取大文件,内存和网络都吃不消

如何才能一点一点?

我们读取文件的时候,可以采用读取完成之后在输出数据

Untitled 7.png

上述说到 stream 继承了 EventEmitter 可以是实现监听数据。首先将读取数据改为流式读取,使用 on("data", ()⇒{}) 接收数据,最后通过 on("end", ()⇒{}) 最后的结果

Untitled 8.png

有数据传递过来的时候就会触发 data 事件,接收这段数据做处理,最后等待所有的数据全部传递完成之后触发 end 事件。

数据的流转过程

数据从哪里来—source

数据是从一个地方流向另一个地方,先看看数据的来源。

  • http 请求,请求接口来的数据

    Untitled 9.png

  • console 控制台,标准输入 stdin

    Untitled 10.png

  • file 文件,读取文件内容,例如上面的例子

连接的管道—pipe

在 source 和 dest 中有一个连接的管道 pipe,基本语法为 source.pipe(dest)

Ändern Um das Stream-Schreiben zu verwenden, lautet der Code wie folgt

Untitled 5.png

Als ich die Anfrage erneut initiierte, stellte ich fest, dass der Speicher nur etwa 35 MB belegte, was im Vergleich zu readFile deutlich reduziert war

Untitled 6.png

Wenn wir den Streaming-Modus nicht verwenden und Warten Sie, bis die große Datei geladen ist, bevor Sie mit dem Betrieb beginnen. Es treten folgende Probleme auf:

    Zu viel Speicher wird vorübergehend verwendet, was zum Absturz des Systems führt

    Die CPU-Betriebsgeschwindigkeit ist begrenzt und dient mehreren Programme sind zu groß und brauchen viel Zeit zum Laden.

    Zusammenfassung Mit anderen Worten: Das gleichzeitige Lesen einer großen Datei ist zu viel für den Speicher und das Netzwerk

    • Wie schaffe ich es Stück für Stück?

      Wenn wir die Datei lesen, können wir die Daten ausgeben, nachdem der Lesevorgang abgeschlossen istUntitled 11.png

      Untitled 7.png🎜🎜Wie oben erwähnt, erbt Stream EventEmitter und kann zur Überwachung von Daten verwendet werden. Ändern Sie zunächst die Lesedaten in Streaming-Lesung, verwenden Sie on("data", ()⇒{}), um die Daten zu empfangen, und verwenden Sie schließlich on("end", ()⇒ { }) Das Endergebnis🎜🎜Untitled 8.png „loading=🎜🎜Wenn Daten übertragen werden, wird das Datenereignis ausgelöst, die Daten werden zur Verarbeitung empfangen und das Endereignis wird ausgelöst, nachdem alle Daten übertragen wurden. 🎜

      🎜Datenflussprozess🎜🎜

      🎜Woher kommen die Daten – Quelle🎜🎜🎜Daten fließen zuerst von einem Ort zum anderen Schauen wir uns die Quelle der Daten an. 🎜🎜🎜🎜http-Anfrage, Daten von der Schnittstelle anfordern🎜🎜Untitled 9 .png🎜

    • 🎜🎜Konsolenkonsole, Standardeingabe stdin🎜🎜Untitled 10.png🎜🎜🎜Dateidatei, lesen Sie den Dateiinhalt, wie im obigen Beispiel🎜

    🎜Verbundene Pipe – Pipe🎜🎜🎜Es gibt eine verbundene Pipe in Quelle und Ziel. Die grundlegende Syntax ist source.pipe(dest), Quelle und Ziel sind über eine Pipe verbunden, sodass Daten von der Quelle zum Ziel fließen können. 🎜🎜Wir müssen das Daten-/Endereignis nicht wie im obigen Code manuell überwachen. Bei der Verwendung von Pipe gelten strenge Anforderungen. Die Quelle muss lesbar sein stream und dest müssen Es ist ein beschreibbarer Stream🎜🎜??? Was genau sind fließende Daten? Was ist ein Chunk im Code? 🎜🎜🎜Wohin – Ziel🎜🎜🎜Drei gängige Ausgabemethoden streamen🎜🎜🎜🎜Konsolenkonsole, Standardausgabe stdout🎜🎜🎜🎜

  • ...

    Ein lesbarer Stream ist eine Abstraktion der Quelle, die Daten bereitstelltUntitled 12.png

    Alle Readables implementieren die durch den Stream definierte Schnittstelle.Readable-Klasse
  • ? Datei-Stream-Erstellung lesenUntitled 13.png

    fs.createReadStream erstellt ein lesbares Objekt
  • Lesemodus

    Untitled 14.png

    Readable Stream verfügt über zwei Modi,

    Flowing-Modusund Pause-Modus

    , die den Flussmodus von Blockdaten bestimmen: automatischer Fluss und manueller Flussfluss

    Es gibt ein _readableState-Attribut in ReadableStream, in dem es ein Fließattribut gibt Es gibt drei Zustandswerte:

    ture: Zeigt den Fließmodus an Untitled 15.png

    false: Zeigt den Pausenmodus an

    null: Anfangszustand

    Untitled 16.png

    Sie können das Warmwasserbereitermodell zur Simulation verwenden der Datenfluss. Der Warmwasserbereiterspeicher (Pufferspeicher) speichert heißes Wasser (erforderliche Daten). Wenn wir den Wasserhahn öffnen, fließt weiterhin heißes Wasser aus dem Wassertank und Leitungswasser fließt weiterhin in den Wassertank Flow-Modus. Wenn wir den Wasserhahn schließen, unterbricht der Wassertank den Wasserzufluss und der Wasserhahn unterbricht die Wasserausgabe. Dies ist der Pausenmodus.

    Flow-Modus

    Daten werden automatisch aus der untersten Ebene gelesen, bilden ein Flussphänomen und werden der Anwendung über Ereignisse bereitgestellt.

    Sie können diesen Modus aufrufen, indem Sie das Datenereignis abhören.

    Wenn das Datenereignis hinzugefügt wird und sich Daten im beschreibbaren Stream befinden, werden die Daten an die Ereignisrückruffunktion übertragen. Sie müssen den Datenblock verbrauchen Wenn es nicht verarbeitet wird, gehen die Daten verloren. Rufen Sie die stream.pipe-Methode auf, um Daten an Writeable zu senden. Rufen Sie die stream.resume-Methode auf Die Daten werden im internen Puffer gesammelt und müssen angezeigt werden. Rufen Sie stream.read() auf, um den Datenblock zu lesen
    • Hören Sie sich das lesbare Ereignis an Der beschreibbare Stream löst diesen Ereignisrückruf aus, nachdem die Daten bereit sind. Zu diesem Zeitpunkt müssen Sie stream.read() in der Rückruffunktion verwenden, um aktiv Daten zu verbrauchen. Das lesbare Ereignis zeigt an, dass der Stream eine neue Dynamik aufweist: Entweder liegen neue Daten vor oder der Stream hat alle Daten gelesen //TODO: Inkonsistent mit der Online-Freigabe
    Pausemodus in Flow-Modus umschalten

    - 监听 data 事件
    - 调用 stream.resume 方法
    - 调用 stream.pipe 方法将数据发送到 Writable
    Nach dem Login kopieren
    Untitled 17.png

    Flow-Modus in Pausenmodus umschalten

    Implementierungsprinzip
    • Erstellen Sie einen lesbaren Stream Wann, wir Sie müssen das Readable-Objekt erben und die _read-Methode implementieren

    • , um einen benutzerdefinierten lesbaren Stream zu erstellen

    • Wenn wir die Read-Methode aufrufen, ist der Gesamtprozess wie folgt:

      Untitled 18.png

    doRead
    A Der Cache wird im Stream verwaltet. Wenn die Lesemethode aufgerufen wird, wird beurteilt, ob Daten von der untersten Ebene angefordert werden müssen

    Wenn die Pufferlänge 0 oder kleiner als der Wert von highWaterMark ist, wird _read aufgerufen, um die Daten von der untersten Ebene abzurufen. Quellcode-Link Daten schreiben Eine Abstraktion des Ziels wird verwendet, um vom Upstream fließende Daten zu verbrauchen und die Daten über einen beschreibbaren Stream auf das Gerät zu schreiben. Ein üblicher Schreibstream ist das Schreiben auf die lokale Festplatte. Merkmale beschreibbarer Streams

    Daten durch Schreiben schreibenUntitled 24.png

    Daten durch Ende schreiben und den Stream schließen, Ende = Schreiben + Schließen

    Untitled 25.png

    Wenn die geschriebenen Daten die Größe von highWaterMark erreichen, wird ein Entleeren ausgelöst Das Ereignis

    • ruft ws.write(chunk) auf und gibt false zurück, was angibt, dass die aktuellen Pufferdaten größer oder gleich dem Wert von highWaterMark sind und das Drain-Ereignis ausgelöst wird. Tatsächlich dient es als Warnung. Wir können weiterhin Daten schreiben, aber die unverarbeiteten Daten werden immer im

      internen Puffer

      des beschreibbaren Streams zurückgehalten. Dies wird nicht erzwungen, bis der Rückstand im Node.js-Puffer voll ist . Unterbrechen: Benutzerdefinierter beschreibbarer Stream Rufen Sie die Methode writable.write auf, um Daten in den Stream zu schreiben. Wenn _write data erfolgreich ist, müssen Sie die nächste Methode aufrufen, um die nächsten Daten zu verarbeiten Rufen Sie writable.end( data) auf, um den beschreibbaren Stream zu beenden, data ist optional. Danach kann das Schreiben nicht mehr aufgerufen werden, um neue Daten hinzuzufügen, andernfalls wird ein Fehler gemeldetUntitled 26.png

      Nachdem die End-Methode aufgerufen wurde und alle zugrunde liegenden Schreibvorgänge abgeschlossen sind, wird das Finish-Ereignis ausgelöst
    • Duplex Stream

      Untitled 27.pngUntitled 28.png Duplex Stream, sowohl lesbar als auch beschreibbar. Tatsächlich handelt es sich um einen Stream, der Readable und Writable erbt. Er kann sowohl als lesbarer Stream als auch als beschreibbarer Stream verwendet werden. Ein benutzerdefinierter Duplex-Stream muss die Methode _readable und die Methode _write implementieren Das

    • net-Modul kann zum Erstellen eines Sockets in NodeJS verwendet werden. Schauen Sie sich ein Beispiel eines TCP-Clients an. Der beschreibbare Stream wird zum Senden von Nachrichten verwendet Der Lesestream wird zum Empfangen von Servernachrichten verwendet.

      Transform StreamUntitled 29.png

      Im obigen Beispiel sind die Daten im lesbaren Stream (0/1) und Die beschreibbaren Daten ('F', 'B', 'B') sind isoliert und es besteht keine Beziehung zwischen den beiden. Bei Transform werden die am beschreibbaren Ende geschriebenen Daten jedoch automatisch zum lesbaren Ende hinzugefügt Transformation. Transform erbt von Duplex und hat bereits die Methoden _write und _read implementiert. Sie müssen nur die Methode _transform implementieren.

    gulp ist ein Stream-basiertes automatisiertes Konstruktionstool Website

    less → less in CSS konvertiert → CSS-Komprimierung durchführen → CSS komprimieren

    Tatsächlich führen less() und minifyCss() beide eine Verarbeitung der Eingabedaten durch und übergeben dann die AusgabedatenUntitled 30.png

      Duplex- und Transformationsoptionen
    • Im Vergleich zum obigen Beispiel stellen wir fest, dass wir uns für Duplex entscheiden, wenn wir nur einige Konvertierungsarbeiten an den Daten durchführen.

      Gegendruckproblem

      Was ist Gegendruck? Das Problem des Gegendrucks ergibt sich aus dem Produzenten-Verbraucher-Modell, bei dem die Verarbeitungsgeschwindigkeit des Verbrauchers zu langsam ist. Während unseres Download-Vorgangs beträgt die Verarbeitungsgeschwindigkeit beispielsweise 3 Mbit/s, während die Verarbeitungsgeschwindigkeit während der Komprimierung zu niedrig ist Prozess, die Verarbeitungsgeschwindigkeit ist zu langsam. Die Geschwindigkeit beträgt 1 Mbit/s. In diesem Fall wird sich die Pufferwarteschlange schnell ansammeln, oder der gesamte Puffer wird langsam sein und einige Daten gehen verloren . Was ist die Gegendruckverarbeitung?

      Die Gegendruckverarbeitung kann als ein Prozess des „Weinens“ nach oben verstanden werden.

      Wenn der Komprimierungsprozess feststellt, dass der Pufferdatendruck den Schwellenwert überschreitet, wird er zur Download-Verarbeitung „schreien“. zu beschäftigt, bitte nicht mehr posten

      Download-Verarbeitung pausiert das Senden von Daten nach unten, nachdem eine Nachricht empfangen wurde

      Untitled 35.png

      So gehen Sie mit Rückstau um

      Wir haben verschiedene Funktionen, um Daten von einem Prozess zum anderen zu übertragen. In Node.js gibt es eine integrierte Funktion namens .pipe(), und letztendlich haben wir auf einer grundlegenden Ebene in diesem Prozess zwei unabhängige Komponenten: die Datenquelle und den Verbraucher

      Wenn .pipe() ist Wird von der Quelle aufgerufen und benachrichtigt den Verbraucher darüber, dass Daten übertragen werden müssen. Die Pipeline-Funktion erstellt ein geeignetes Backlog-Paket zum Auslösen von Ereignissen

      Wenn der Datencache HighWaterMark überschreitet oder die Schreibwarteschlange beschäftigt ist, gibt .write() false zurück.

      Wenn false zurückgegeben wird, greift das Backlog-System ein. Es pausiert eingehende Readables von jedem Datenstrom, der Daten sendet. Sobald der Datenstrom geleert ist, wird das Drain-Ereignis ausgelöst und der eingehende Datenstrom wird verbraucht. Sobald die Warteschlange vollständig verarbeitet ist, ermöglicht der Rückstandsmechanismus das erneute Senden der Daten. Der verwendete Speicherplatz wird freigegeben und bereitet sich auf den Empfang des nächsten Datenstapels vor

      Untitled 36.png

      Wir können die Gegendruckverarbeitung der Pipe sehen:

      Teilen Sie die Daten in Blöcke auf und schreiben Sie

      Wenn der Block durchläuft Wenn die Warteschlange zu groß oder ausgelastet ist, wird das Lesen angehalten

      Wenn die Warteschlange leer ist, lesen Sie die Daten weiter

      Weitere Informationen zu Knoten finden Sie unter:

      nodejs-Tutorial

      !

    Das obige ist der detaillierte Inhalt vonEine eingehende Analyse von Stream in Node. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!

    Verwandte Etiketten:
    Quelle:juejin.cn
    Erklärung dieser Website
    Der Inhalt dieses Artikels wird freiwillig von Internetnutzern beigesteuert und das Urheberrecht liegt beim ursprünglichen Autor. Diese Website übernimmt keine entsprechende rechtliche Verantwortung. Wenn Sie Inhalte finden, bei denen der Verdacht eines Plagiats oder einer Rechtsverletzung besteht, wenden Sie sich bitte an admin@php.cn
    Beliebte Tutorials
    Mehr>
    Neueste Downloads
    Mehr>
    Web-Effekte
    Quellcode der Website
    Website-Materialien
    Frontend-Vorlage
    Über uns Haftungsausschluss Sitemap
    Chinesische PHP-Website:Online-PHP-Schulung für das Gemeinwohl,Helfen Sie PHP-Lernenden, sich schnell weiterzuentwickeln!