Stas Afanasyev. Juno. Pipelines basierend auf io.Reader / io.Writer. Teil 2

In dem Bericht werden wir über das Konzept von io.Reader / io.Writer sprechen, warum sie benötigt werden, wie sie korrekt implementiert werden und welche Fallstricke diesbezüglich bestehen, sowie über das Erstellen von Pipelines basierend auf Standard- und benutzerdefinierten io.Reader / io.Writer-Implementierungen .



Stas Afanasyev. Juno. Pipelines basierend auf io.Reader / io.Writer. Teil 1

Fehler "auf Vertrauen"


Eine weitere Nuance: In dieser Implementierung gibt es einen „Bagul“. Dieser Fehler wird von den Entwicklern bestätigt (ich habe ihnen darüber geschrieben). Vielleicht weiß jemand, was dieser "Bagul" ist? Es auf der Folie ist die vorletzte Zeile:



Es ist mit zu viel Vertrauen in den umschlossenen Reader verbunden: Wenn der Reader eine negative Anzahl von Bytes zurückgibt, erhöht sich das Limit, das wir durch die Anzahl der subtrahierten Bytes erhalten möchten. In einigen Fällen handelt es sich um einen ziemlich schwerwiegenden Fehler, den Sie nicht sofort verstehen können.

Ich schrieb in der Ausgabe: Lass uns etwas tun, lass es uns reparieren! Und dann wurde eine Schicht von Problemen aufgedeckt ... Zuerst sagten sie mir, wenn Sie diesen Scheck jetzt hier hinzufügen, müssen Sie diesen Scheck überall hinzufügen, und es gibt ein Dutzend dieser Orte. Wenn wir dies auf die Clientseite verschieben möchten, müssen wir eine Reihe von Regeln festlegen, nach denen der Client die Daten validiert (und es können auch fünf oder zwei davon sein). Es stellt sich heraus, dass all dies kopiert werden muss.

Ich bin damit einverstanden, dass dies nicht optimal ist. Dann kommen wir zu einer konsistenten Version! Warum haben wir eine Implementierung der Standardbibliothek, die nichts vertraut, während andere absolut alles vertrauen?

Während ich meine bürgerliche Meinung schrieb und darüber nachdachte, schlossen wir das Thema im Allgemeinen mit Kommentaren ab: „Wir werden nichts tun. Auf Wiedersehen"! Sie haben mich wie eine Art Dummkopf aussehen lassen ... Politisch kann man natürlich keinen Fehler finden.

Im Allgemeinen haben wir jetzt ein Problem. Es besteht darin, dass nicht klar ist, wer die Daten des umschlossenen Readers validieren soll. Entweder der Kunde oder wir vertrauen voll und ganz dem Vertrag ... Wir haben eine Lösung! Wenn noch Zeit ist, werde ich davon erzählen.

Fahren wir mit dem nächsten Fall fort.

Teereader


Wir haben uns ein Beispiel für das Umschließen von Reader-Daten angesehen. Das nächste Beispiel für Pipes ist das Überholen von Reader-Daten in Writer. Es gibt zwei Situationen.

Erste Situation. Wir müssen die Daten aus Reader lesen, sie irgendwie (transparent) in Writer kopieren und damit arbeiten wie mit Reader. Hierfür gibt es eine Implementierung von TeeReader. Es wird im oberen Implementierungs-Snippet vorgestellt:



Funktioniert wie das Tee-Team unter Unix. Ich denke, viele von Ihnen haben davon gehört.
Beachten Sie, dass diese Implementierung die Anzahl der Bytes überprüft, die vom umschlossenen Reader gelesen werden. Sehen Sie die Bedingungen in der zweiten Zeile? Denn wenn Sie eine solche Implementierung schreiben, ist intuitiv klar: Bei einer negativen Zahl geraten Sie in Panik. Und dies ist ein weiterer Ort, an dem wir dem verpackten Reader vertrauen! Ich erinnere Sie daran, dass dies alles Standardbibliotheken sind.

Kommen wir zum Beispiel zu einem Fall, wie man ihn benutzt. Was machen wir mit dem unteren Snippet? Wir werden die robot.txt-Datei von golang.org mit dem Standard-http-Client herunterladen.

Wie Sie wissen, gibt der http-Client eine Antwortstruktur an uns zurück, in der das Body-Feld eine Implementierung der Reader-Schnittstelle ist. Es sollte klargestellt werden, dass dies eine Implementierung der ReadCloser-Schnittstelle ist. ReadCloser ist jedoch nur eine Schnittstelle, die aus Reader und Closer besteht. Das heißt, dies ist ein Reader, der im Allgemeinen geschlossen werden kann.

In diesem Beispiel (im unteren Snippet) sammeln wir TeeReader, der Daten aus diesem Body liest und in eine Datei schreibt. Die Erstellung der Datei blieb heute leider hinter den Kulissen, da nicht alles passte. Wenn Sie sich jedoch das Dendrogramm ansehen, implementiert der Dateityp die Writer-Schnittstelle, dh wir können darauf schreiben. Es ist offensichtlich.

Wir haben unseren TeeReader zusammengebaut und mit ReadAll gelesen. Alles funktioniert wie erwartet: Wir subtrahieren den resultierenden Body, schreiben ihn in eine Datei und sehen ihn in Assad out.

Anfänger Weg


Die zweite Situation. Wir müssen nur die Daten aus dem Reader lesen und in den Writer schreiben. Die Lösung liegt auf der Hand ...

Als ich gerade mit Go angefangen habe, habe ich Probleme wie auf einer Folie gelöst:



Ich habe den Puffer gefunden, ihn mit Daten aus dem Reader gefüllt und das gefüllte Slice an Writer übertragen. Alles ist einfach.

Zwei Punkte. Erstens gibt es keine Garantie dafür, dass der gesamte Reader in einem Aufruf der Read-Methode subtrahiert wird, da möglicherweise noch Daten übrig sind (dies sollte in einer Schleife erfolgen).

Der zweite Punkt ist, dass dieser Weg nicht optimal ist. Hier ist hübscher Boilerplate-Code, der vor uns geschrieben wurde.

Zu diesem Zweck gibt es in der Standardbibliothek eine spezielle Familie von Helfern: Copy, CopyN und CopyBuffer.

io.Copy. WriterTo und ReaderFrom


io.Copy macht im Grunde das, was es auf der vorherigen Folie war: Es weist einen Standardpuffer von 32 KB zu und schreibt Daten vom Reader zum Writer (die Signatur dieser Kopie wird im oberen Snippet angezeigt):



Zusätzlich zu dieser Vorlagenroutine enthält es auch eine Reihe kniffliger Optimierungen. Bevor wir über diese Optimierungen sprechen, müssen wir uns mit zwei weiteren Schnittstellen vertraut machen:

  • WriterTo;
  • ReadFrom.

Hypothetische Situation. Ihr Reader arbeitet mit einem Speicherpuffer. Er hat es bereits verlegt, schreibt, liest etwas von dort, das heißt, ein Ort darunter wurde bereits verlegt. Sie möchten diesen Reader irgendwo von außen lesen.

Wir haben bereits gesehen, wie dies geschieht: Ein Puffer wird erstellt, der Puffer wird übergeben, der an die Read-Methode übergeben wird; Der Reader, der mit dem Gedächtnis arbeitet, wirft es aus dem replizierten Stück heraus ... Aber das ist nicht mehr optimal - der Ort wurde neu positioniert. Warum nochmal?



Vor 5-6 Jahren (es gibt einen Link zur Änderungsliste) wurden zwei Schnittstellen erstellt: WriteTo und ReadFrom, die lokal implementiert sind. Reader implementiert WriteTo und Writer implementiert ReadFrom. Es stellt sich heraus, dass Reader mit einem Slice mit bereits replizierten Daten einen zusätzlichen Speicherort vermeiden und Write To Writer-Methoden akzeptieren und einen darin verfügbaren Puffer übergeben kann.

So funktioniert die Implementierung von bytes.Buffer und bufio. Und wenn Sie sich das Dendrogramm noch einmal ansehen, werden Sie feststellen, dass diese beiden Schnittstellen nicht sehr beliebt sind. Sie werden nur für diejenigen Typen implementiert, die mit dem internen Puffer arbeiten - wo der Speicher bereits verschoben ist. Dies hilft Ihnen nicht, Beredsamkeit jedes Mal zu vermeiden, sondern nur, wenn Sie bereits mit einem umgesiedelten Stück arbeiten.

ReaderFrom funktioniert ähnlich (es wird nur von Writer implementiert). ReaderFrom liest den gesamten Reader, der als Argument (vor EOF) dient, und schreibt irgendwo in die interne Implementierung von Writer.

CopyBuffer-Implementierung


Dieses Snippet zeigt die Implementierung des copyBuffer-Hilfsprogramms. Dieser nicht exportierbare copyBuffer wird unter der Haube von io.Copy, CopyN und CopyBuffer verwendet.

Und hier gibt es eine kleine Nuance, die es wert ist, erwähnt zu werden. CopyN wurde kürzlich optimiert - unabhängig von dieser Logik. Dies ist genau die Optimierung, über die ich zuvor gesprochen habe: Bevor ein zusätzlicher Puffer von 32 KB erstellt wird, wird eine Überprüfung durchgeführt - möglicherweise implementiert die Datenquelle die WriterTo-Schnittstelle, und dieser zusätzliche Puffer wird nicht benötigt?

Wenn dies nicht geschieht, überprüfen wir: Vielleicht implementiert Writer ReaderFrom, um sie ohne diesen Vermittler zu verbinden? Wenn dies nicht geschieht, bleibt die letzte Hoffnung: Vielleicht haben wir eine Art verschobenen Puffer erhalten, den wir verwenden könnten?



So funktioniert io.Copy.

Es gibt ein Problem, nämlich einen Halbvorschlag, einen Halbfehler - es ist nicht klar, was. Es hängt seit anderthalb Jahren. Es klingt so: CopyBuffer ist semantisch falsch.

Leider gibt es keine Signatur für diesen copyBuffer, aber es sieht genauso aus wie bei dieser nicht exportierbaren Methode.

Wenn Sie copyBuffer aufrufen, um einen zusätzlichen Speicherort zu vermeiden, übertragen Sie dort eine Art verschobenes Slice-Byte. Die folgende Logik funktioniert: Wenn Reader oder Writer die Schnittstellen WriterTo und ReaderFrom implementieren, gibt es keine Garantie dafür, dass Sie diesen Speicherort vermeiden können. Dies wurde als Vorschlag angenommen und versprochen, in Go 2.0 darüber nachzudenken. Im Moment müssen Sie nur wissen.

Arbeiten Sie mit io.Pipe. PipeReader und pipeWriter


Ein anderer Fall: Sie müssen Daten von Writer irgendwie in Reader erhalten. Hübscher Lebensfall.

Stellen Sie sich vor, Sie haben bereits einige Daten, sie implementieren die Reader-Oberfläche - damit ist alles klar. Sie müssen diese Daten komprimieren, optimieren und an S3 senden. Was ist die Nuance? ..
Wer mit dem gzip-Typ im Compess-Paket gearbeitet hat, weiß, dass der gzip'er selbst nur ein Proxy ist: Er nimmt Daten in sich auf, implementiert die Writer-Schnittstelle, schreibt die Daten, etwas wird mit ihnen tun, und dann muss ich sie irgendwo fallen lassen. Auf dem Konstruktor ist eine Implementierung der Writer-Schnittstelle erforderlich.

Dementsprechend benötigen wir hier eine Art Zwischenschreiber, in dem wir die bereits komprimierten Daten löschen, die in der ersten Stufe archiviert werden. Unser nächster Schritt ist das Hochladen dieser Daten in S3. Der Standard-AWS-Client akzeptiert die io.Reader-Schnittstelle als Datenquelle.



Die Folie zeigt die Pipeline - sie zeigt, wie sie aussieht: Wir müssen die Daten überholen, um von Reader zu Writer, von Writer zu Reader zu überholen. Wie kann man das machen?

Die Standardbibliothek hat eine coole Funktion - io.Pipe. Es werden zwei Werte zurückgegeben: pipeReader und pipeWriter. Dieses Paar ist untrennbar miteinander verbunden. Stellen Sie sich ein „Baby-Telefon“ in Tassen mit Seilen vor: Es macht keinen Sinn, in einer Tasse zu sprechen, während am anderen Ende niemand zuhört ...



Was macht diese io.Pipe? Es wird nicht gelesen, bis niemand die Daten schreibt. Und umgekehrt wird er nichts schreiben, bis niemand diese Daten am anderen Ende liest. Hier ist eine Beispielimplementierung:



Wir werden das Gleiche hier tun. Wir werden die zuvor gelesene Datei robot.txt lesen, sie mit unserem gzip komprimieren und an S3 senden.

  • In der ersten Zeile wird ein Paar erstellt - pipeReader, pipeWriter. Als nächstes müssen wir mindestens eine Goroutine ausführen, die Daten von einem Ende liest (eine Art Pipe). Führen Sie in diesem Gorutin den Uploader mit einer Datenquelle (source - pipeReader) aus.
  • Im nächsten Schritt müssen wir die Daten komprimieren. Wir komprimieren die Daten und schreiben sie in pipeWriter (es wird das andere Ende der Pipe sein), und bereits laufende Goroutine empfängt die Daten am anderen Ende der Pipe und liest sie. Wenn dieses ganze Sandwich fertig ist, bleibt nur noch, den Docht in Brand zu setzen ...
  • Siehe: io.Copy in der letzten Zeile schreibt Daten vom Body in das von uns erstellte gzip (d. H. Vom Reader zum Writer). All dies funktioniert wie erwartet.

Dieses Beispiel kann auf andere Weise gelöst werden. Wenn Sie eine Implementierung verwenden, die sowohl Reader als auch Writer implementiert. Sie schreiben zuerst Daten hinein und lesen sie dann.
Es war eine klare Demonstration der Arbeit mit io.Pipe.

Andere Implementierungen


Das ist im Grunde alles für mich. Wir kommen zu interessanten Implementierungen, über die ich sprechen möchte.



Ich habe weder zu MultiReader noch zu MultiWriter etwas gesagt. Und dies ist eine weitere coole Implementierung der Standardbibliothek, mit der Sie verschiedene Implementierungen verbinden können. Beispielsweise schreibt MultiWriter gleichzeitig in alle Writer und MultiReader liest die Reader nacheinander.

Eine andere Implementierung heißt limio. Hier können Sie ein Limit für die Subtraktion festlegen. Sie können die Geschwindigkeit in Bytes pro Sekunde einstellen, mit der Ihr Reader gelesen werden muss.

Eine weitere interessante Implementierung ist nur eine Visualisierung des Lesefortschritts - der Fortschrittsbalken (von einem Typen). Es heißt ioprogress.

Warum habe ich das alles gesagt? Was habe ich damit gemeint?



  • Wenn Sie plötzlich die Reader- und Writer-Schnittstellen implementieren müssen, machen Sie es richtig. Es gibt noch keine einzige Entscheidung, wer für die Implementierung verantwortlich ist - wir gehen davon aus, dass jeder dem Vertrag vertraut. Sie müssen sich also tadellos daran halten.
  • Wenn Ihr Fall mit einem neu positionierten Puffer arbeitet, vergessen Sie nicht die Schnittstellen ReaderFrom und WriterTo.
  • Wenn Sie sich in einer Sackgasse befinden und Beispiele benötigen - siehe Standardbibliothek - gibt es viele coole Implementierungen, auf die Sie sich verlassen können. Dort gibt es Dokumentation.
  • Wenn Ihnen etwas völlig unverständlich ist, können Sie gerne Probleme schreiben. Die Leute dort sind angemessen, reagieren schnell, sehr höflich und helfen Ihnen kompetent.



Das ist alles für mich. Danke fürs Kommen!

Fragen


Frage des Publikums (B): - Ich habe eine einfache Frage, denke ich. Bitte erzählen Sie uns etwas über einige Anwendungsfälle aus dem Leben: Welche wurden verwendet und warum? Sie sagten, dass Reader / Writer die Länge zurückgibt, die es gelesen hat. Haben Sie jemals Probleme damit gehabt? Wann haben Sie nach Lesen gefragt (nicht nur ReadAll existiert), aber etwas hat nicht funktioniert?

SA: - Ich muss ehrlich zugeben, dass ich solche Fälle nie hatte, weil ich immer mit Implementierungen der Standardbibliothek gearbeitet habe. Aber hypothetisch ist eine solche Situation natürlich möglich. In bestimmten Fällen sammeln wir häufig mehrschichtige Rohre. Wenn Sie einen solchen Fehler hypothetisch zulassen, fällt das gesamte Rohr auseinander ...

F:- Das ist kein Fehler. Dann erzählen wir Ihnen von meiner kleinen Erfahrung. Ich hatte ein Problem mit Booking.com: Sie verwendeten den von mir geschriebenen Treiber und hatten ein Problem - etwas funktionierte nicht. Es gibt ein Standard-Binärprotokoll, das wir erstellt haben. lokal funktioniert alles gut, allen geht es gut, aber es stellte sich heraus, dass sie ein sehr schlechtes Netzwerk mit einem Rechenzentrum haben. Dann hat Reader nicht wirklich alles zurückgegeben (schlechte Netzwerkkarten, etwas anderes).

CA: - Aber wenn er nicht alles zurückgegeben hat, hätte er das Zeichen des Endes (des Endes) nicht zurückgeben sollen, und der Kunde sollte wiederkommen. Unter dem beschriebenen Vertrag sollte der Leser nicht ... Sagen wir einfach, dass der Leser natürlich entscheidet, wann er kommen möchte, wann er nicht möchte, aber wenn er alles lesen möchte, muss er auf EOF warten.

BEIM:"Aber genau das liegt an der Verbindung." Dies ist genau das Problem, das im Standardnetzpaket aufgetreten ist.

CA: - Und er hat die EOF zurückgegeben?

F: - Er hat nicht alles zurückgegeben - er hat einfach nicht alles gelesen. Ich sagte ihm: "Lies die nächsten 20 Bytes." Er liest. Und ich lese nicht alles.

SA: - Hypothetisch ist dies möglich, da es sich nur um eine Schnittstelle handelt, die ein Kommunikationsprotokoll beschreibt. Es ist notwendig, den Fall zu beobachten und speziell zu zerlegen. Hier kann ich Ihnen nur antworten, dass der Kunde theoretisch wieder hätte kommen sollen, wenn er nicht alles erhalten hätte, was er wollte. Sie haben ihn um eine Scheibe von 20 Bytes gebeten, er hat 15 für Sie abgezogen, aber EOF ist nicht gekommen - Sie sollten noch einmal gehen ...

F: - Für diese Situation gibt es io.ReadFull. Es wurde speziell entwickelt, um die Scheibe bis zum Ende zu lesen.

CA:- Ja. Ich habe nichts über ReadFull gesagt.

F: - Dies ist eine völlig normale Situation, wenn Read nicht den gesamten Slice ausfüllt. Sie müssen darauf vorbereitet sein.

SA: - Dies ist ein sehr erwarteter Fall!

F: - Danke für den Bericht - es war interessant. Ich verwende Readers in einem kleinen, einfachen Proxy, der http liest und in die andere Richtung schreibt. Ich benutze Close Reader, um ein Problem zu lösen - um das zu schließen, was ich ständig lese. Muss ich einem Vertrag blind vertrauen? Sie sagten, dass es Probleme geben könnte. Oder zusätzliche Schecks hinzufügen? Es ist theoretisch möglich, dass etwas auf dieser Seite nicht vollständig kommt. Muss ich diese zusätzlichen Überprüfungen durchführen und dem Vertrag nicht vertrauen?

CA:- Ich würde Folgendes sagen: Wenn Ihre Anwendung diese Fehler toleriert (z. B. wenn Sie dem Vertrag voll vertrauen), dann möglicherweise nicht. Aber wenn Sie keine „Panik“ in sich haben möchten (wie ich bei einem negativen Messwert in Byte.Buffer gezeigt habe), würde ich trotzdem nachsehen.
Aber das liegt ganz bei Ihnen. Was kann ich Ihnen empfehlen? Ich denke, nur die Vor- und Nachteile abwägen. Was passiert, wenn Sie plötzlich eine negative Anzahl von Bytes erhalten?

F: - Danke für den Bericht. Leider weiß ich nichts in Go. Wenn eine „Panik“ aufgetreten ist, gibt es eine Möglichkeit, diese Informationen abzufangen und Informationen darüber zu erhalten, was, wo und wie man voreingenommen ist, um Probleme am Freitagabend zu vermeiden?

CA: - Ja. Der Wiederherstellungsmechanismus ermöglicht es Ihnen, eine Panik zu "fangen" und sie zu beseitigen, ohne zu fallen, relativ gesehen.



BEIM:- Wie stimmen Ihre Empfehlungen für die Verwendung von Implementierungen von Writer und Reader mit den Fehlern überein, die bei der Implementierung von Web-Sockets zurückgegeben werden? Ich werde kein konkretes Beispiel nennen, aber wird dort immer das Dateiende verwendet? Soweit ich mich erinnere, endet die Nachricht mit einigen anderen Bedeutungen ...

SA: - Dies ist eine gute Frage, weil ich einfach nichts zu beantworten habe. Müssen beobachten! Wenn EOF nicht kommt, muss der Kunde, wenn er alles bekommen will, wieder gehen.

F: - Wie lange konnte das Rohr zusammengebaut werden? Gibt es interne Überzeugungen, dass es sich nicht lohnt, mehr als fünf Teilnehmer oder Zweigstellen zu sammeln? Wie lange haben Sie es geschafft, aus diesen Rohren einen Baum zu bauen (Lesen, Schreiben)?

CA:- In meiner Praxis sind ungefähr fünf aufeinanderfolgende Anrufe optimal, da das Debuggen schwieriger ist. Denken Sie daran, was fließt und wohin es geht. Man erhält eine ziemlich verzweigte Struktur. Aber ich würde irgendwo maximal 5-7 sagen.

F: - 5-7 - in welchem ​​Fall?

SA: - Dies liest zum Beispiel einige Daten. Sie müssen versprechen, und was Sie anmelden, müssen Sie trimmen. Verpfändet - dann lesen Sie diese Daten - müssen Sie sie an einen Speicher zurücksenden (na ja, hypothetisch). In jedem Speicher, den die Writer-Schnittstelle implementiert. Bei dieser Pipe treten 5-6 Schritte auf, obwohl sie bei einem der Schritte immer noch zur Seite abzweigt und Sie weiterhin mit Reader arbeiten.

BEIM:- Nach dem Anfängerweg hatten Sie eine interessante Folie. Können Sie weitere 2-3 interessante Punkte angeben, die es gab, aber jetzt ist es besser, sie nicht zu tun, sondern jetzt anders zu machen?

SA: - Mit dieser Folie wollte ich genau zeigen, wie es geht, ohne Reader lesen zu müssen. Mir ist nie in den Sinn gekommen, dass so etwas wie der Anfänger-Weg ... Dies ist wahrscheinlich der Hauptfehler, das Hauptmuster, das bei der Arbeit mit Lesern vermieden werden sollte.
Moderator: - Ich möchte selbst hinzufügen, dass es für Anfänger sehr wichtig ist, die gesamte Dokumentation des io-Pakets auf allen vorhandenen Schnittstellen zu lesen und zu verstehen. Denn tatsächlich gibt es viele davon, und Sie beginnen oft, etwas Eigenes zu tun, obwohl es dort bereits vorhanden und korrekt implementiert ist („richtig“ - unter Berücksichtigung aller Funktionen).
Frage des Führers: - Wie kann man weiter leben?

CA: - Gute Frage! Ich habe versprochen zu sagen, ob wir Zeit haben. Als Ergebnis der Diskussion des Fehlers traf LimitedReader die folgende Entscheidung: Um ein Reader-Kondom herzustellen, das in gewisser Weise vor externen Bedrohungen schützt, wickeln Sie einen Reader ein, dem Sie nicht vertrauen - um zu verhindern, dass eine Infektion in Ihr System eindringt.

Und in diesem Reader implementieren Sie alle Überprüfungen, die Sie nicht durchführen können: zum Beispiel negatives Lesen, Experimente mit der Anzahl der Bytes (Nehmen wir an, Sie haben einen Slice von 10 Bytes gesendet und 15 zurückbekommen - wie sollen Sie darauf reagieren?) ... Reader und Sie können eine Reihe solcher Überprüfungen implementieren. Ich sagte: "Vielleicht wollen wir die Standardbibliothek erweitern, weil es für alle nützlich wäre, sie zu verwenden."

Mir wurde die Antwort gegeben, dass dies keinen Sinn zu haben scheint - dies ist eine einfache Sache, die Sie selbst implementieren können. Alle. Wir leben weiter. Wir vertrauen den Vertragsleuten. Aber ich würde nicht vertrauen.



F: - Wenn wir mit Lesern, Schriftstellern zusammenarbeiten und die Möglichkeit besteht, auf eine gzip-Bombe zu stoßen ... Wie sehr vertrauen wir auf ReadAll und WriteAll? Oder trotzdem Pufferlesung implementieren und nur mit dem Puffer arbeiten?

CA:- ReadAll selbst verwendet nur Bytes. Puffer unter der Haube. Wenn Sie dieses oder jenes Ding verwenden möchten, ist es ratsam, einzusteigen und zu sehen, wie diese "Eingeweide" umgesetzt werden. Auch dies hängt von Ihren Anforderungen ab: Wenn Sie solche Fehler, die ich gezeigt habe, nicht tolerieren, müssen Sie prüfen, ob überprüft wird, was vom verpackten Reader kommt. Wenn es nicht aktiviert ist, verwenden Sie zum Beispiel bufio (dort ist alles aktiviert). Oder tun Sie, was ich gerade gesagt habe: einen bestimmten Proxy-Reader, der diese Daten gemäß Ihrer Anforderungsliste überprüft und entweder an den Client oder an den Client zurückgibt.




Ein bisschen Werbung :)


Vielen Dank für Ihren Aufenthalt bei uns. Gefällt dir unser Artikel? Möchten Sie weitere interessante Materialien sehen? Unterstützen Sie uns, indem Sie eine Bestellung aufgeben oder Ihren Freunden Cloud-basiertes VPS für Entwickler ab 4,99 US-Dollar empfehlen , ein einzigartiges Analogon von Einstiegsservern, das von uns für Sie erfunden wurde: Die ganze Wahrheit über VPS (KVM) E5-2697 v3 (6 Kerne) 10 GB DDR4 480 GB SSD 1 Gbit / s ab 19 $ oder wie teilt man den Server? (Optionen sind mit RAID1 und RAID10, bis zu 24 Kernen und bis zu 40 GB DDR4 verfügbar).

Dell R730xd 2-mal günstiger im Equinix Tier IV-Rechenzentrum in Amsterdam? Nur wir haben 2 x Intel TetraDeca-Core Xeon 2x E5-2697v3 2,6 GHz 14C 64 GB DDR4 4 x 960 GB SSD 1 Gbit / s 100 TV von 199 US-Dollar in den Niederlanden!Dell R420 - 2x E5-2430 2,2 GHz 6C 128 GB DDR3 2x960 GB SSD 1 Gbit / s 100 TB - ab 99 US-Dollar! Lesen Sie mehr über den Aufbau eines Infrastrukturgebäudes. Klasse C mit Dell R730xd E5-2650 v4-Servern für 9.000 Euro für einen Cent?

All Articles