Im Internet liegt wieder jemand falsch - in der gestrigen Node Weekly gab es einen Link zu einem Beitrag, in dem der Autor versucht, die Leistung der Stream-API in Node.js zu messen und zu vergleichen. Traurigkeit bewirkt, wie der Autor mit Streams arbeitet und welche Schlussfolgerungen er daraus zu ziehen versucht:
... das hat bei kleineren Dateien ziemlich gut funktioniert, aber als ich zur größten Datei kam, ist der gleiche Fehler aufgetreten. Obwohl Node.js die Ein- und Ausgänge gestreamt hat, wurde dennoch versucht, die gesamte Datei während der Ausführung der Vorgänge im Speicher zu halten
Versuchen wir herauszufinden, was mit den Schlussfolgerungen und dem Code des Autors nicht stimmt.
Aus meiner Sicht besteht das Problem darin, dass der Autor des Artikels nicht weiß, wie man Stream'ami verwendet, und dies ist ein Problem, mit dem man sich ziemlich oft befassen muss. Dieses Phänomen hat meiner Meinung nach drei Gründe:
- Die komplexe Geschichte der Node.js Stream API - die hier beschriebenen Schmerzen und Leiden
- Nicht die intuitivste API, wenn Sie versuchen, sie ohne Wrapper zu verwenden
- Ziemlich seltsame Dokumentation, die Streams als etwas sehr Komplexes und Niedriges darstellt
Alles in allem führt dies dazu, dass Entwickler häufig nicht wissen, wie und die Stream-API nicht verwenden möchten.
Was ist los mit dem Autorencode ?
Wiederholen wir zunächst die Aufgabe hier (das Original in englischer Sprache und ein Link zur Datei finden Sie im Beitrag):
Es gibt eine bestimmte 2,5-GB-Datei mit Zeilen des Formulars:
C00084871|N|M3|P|201703099050762757|15|IND|COLLINS, DARREN ROBERT|SOUTHLAKE|TX|760928782|CELANESE|VPCHOP&TECH|02282017|153||PR2552193345215|1151824||P/R DEDUCTION ($76.92 BI-WEEKLY)|4030920171380058715
Sie müssen es analysieren und die folgenden Informationen herausfinden:
- Die Anzahl der Zeilen in der Datei
- Namen in der 432. und 43243. Zeile (hier stellt sich die Wahrheit, wie man zählt, von 0 oder 1?)
- Der gebräuchlichste Name und wie oft er vorkommt
- Die Anzahl der Raten für jeden Monat
Was ist das Problem? - Der Autor sagt ehrlich, dass er die gesamte Datei in den Speicher lädt. Aus diesem Grund „hängt“ der Knoten und der Autor gibt uns eine interessante Tatsache.
Unterhaltsame Tatsache: Node.js kann jeweils nur bis zu 1,67 GB Speicher speichern
Der Autor zieht aus dieser Tatsache eine seltsame Schlussfolgerung, dass es Streams sind, die die gesamte Datei in den Speicher laden, und er hat nicht den falschen Code geschrieben.
Lassen Sie uns die These widerlegen: " Obwohl Node.js die Ein- und Ausgänge gestreamt hat, wird immer noch versucht, die gesamte Datei zu speichern ", indem wir ein kleines Programm schreiben, das die Anzahl der Zeilen in einer Datei beliebiger Größe zählt:
const { Writable } = require('stream') const fs = require('fs') const split = require('split') let counter = 0 const linecounter = new Writable({ write(chunk, encoding, callback) { counter = counter + 1 callback() }, writev(chunks, callback) { counter = counter + chunks.length callback() } }) fs.createReadStream('itcont.txt') .pipe(split()) .pipe(linecounter) linecounter.on('finish', function() { console.log(counter) })
NB : Der Code wurde absichtlich so einfach wie möglich geschrieben. Globale Variablen sind schlecht!
Worauf Sie achten sollten:
- split - npm ein Paket, das einen Zeilenstrom am „Eingang“ empfängt - gibt einen Strom von Leitungssätzen mit einem getrennten Zeilenumbruch an den „Ausgang“ zurück. Höchstwahrscheinlich als Implementierung des Transformationsstroms gemacht. Wir leiten unseren ReadStream mit einer Datei hinein und leiten uns in ...
- linecounter - Implementierung von WritableStream. Darin implementieren wir zwei Methoden: zum Verarbeiten eines Stücks (Chunk) und mehrerer. Die "Zeile" in dieser Situation ist die Codezeile. Umkehren - Fügen Sie die gewünschte Zahl zum Zähler hinzu. Es ist wichtig zu verstehen, dass in dieser Situation nicht die gesamte Datei in den Speicher geladen wird und die API alles für uns in „Teile“ unterteilt, die für die Verarbeitung am bequemsten sind
- 'finish' - Ereignisse, die "eintreten", wenn die in unserem ReadableStream eintreffenden Daten "enden". In diesem Fall verpfänden wir Zählerdaten
Lassen Sie uns unsere Kreation an einer großen Datei testen:
> node linecounter.js 13903993
Wie Sie sehen können, funktioniert alles. Daraus können wir schließen, dass die Stream-API bei Dateien jeder Größe hervorragende Arbeit leistet und die Aussage des Autors des Beitrags, gelinde gesagt, nicht wahr ist. Auf ungefähr die gleiche Weise können wir jeden anderen für das Problem erforderlichen Wert berechnen.
Sagen Sie:
- Möchten Sie lesen, wie Sie das Problem vollständig lösen und den resultierenden Code für die Wartung in eine bequeme Form bringen können?
- Verwenden Sie die Stream-API und auf welche Schwierigkeiten sind Sie gestoßen?