One place for hosting & domains

      May 2020

      Verwenden von Puffern in Node.js.


      Der Autor hat den COVID-19 Relief Fund dazu ausgewählt, eine Spende im Rahmen des Programms Write for DOnations zu erhalten.

      Einführung

      Ein Puffer ist ein Speicherplatz (normalerweise RAM), in dem Binärdaten gespeichert werden. In Node.js können wir mit der integrierten Puffer-Klasse auf diese Speicherbereiche zugreifen. Puffer speichern eine Folge von Ganzzahlen, ähnlich einem Array in JavaScript. Im Gegensatz zu Arrays können Sie die Größe eines Puffers nach seiner Erstellung nicht mehr ändern.

      Möglicherweise haben Sie implizit Puffer verwendet, wenn Sie bereits Node.js-Code geschrieben haben. Wenn Sie beispielsweise mit fs.readFile() aus einer Datei lesen, sind die an den Rückruf oder das Versprechen zurückgegebenen Daten ein Pufferobjekt. Wenn HTTP-Anforderungen in Node.js gestellt werden, geben sie außerdem Datenströme zurück, die vorübergehend in einem internen Puffer gespeichert sind, wenn der Client den Stream nicht auf einmal verarbeiten kann.

      Puffer sind nützlich, wenn Sie mit Binärdaten interagieren, normalerweise auf niedrigeren Netzwerkebenen. Sie bieten Ihnen auch die Möglichkeit, feinkörnige Datenmanipulationen in Node.js durchzuführen.

      In diesem Tutorial verwenden Sie Node.js REPL, um verschiedene Beispiele für Puffer durchzugehen, z. B. das Erstellen von Puffern, das Lesen aus Puffern, das Schreiben in und das Kopieren aus Puffern sowie die Verwendung von Puffern zum Konvertieren zwischen binären und codierten Daten. Am Ende des Tutorials haben Sie gelernt, wie Sie mit der Puffer-Klasse mit Binärdaten arbeiten.

      Voraussetzungen

      Schritt 1 — Erstellen eines Puffers

      Dieser erste Schritt zeigt Ihnen die beiden wichtigsten Möglichkeiten zum Erstellen eines Pufferobjekts in Node.js.

      Um zu entscheiden, welche Methode verwendet werden soll, müssen Sie die folgende Frage beantworten: Möchten Sie einen neuen Puffer erstellen oder einen Puffer aus vorhandenen Daten extrahieren? Wenn Sie Daten im Speicher speichern möchten, die Sie noch nicht empfangen haben, sollten Sie einen neuen Puffer erstellen. In Node.js verwenden wir dazu die Funktion alloc() der Puffer-Klasse.

      Öffnen wir die Node.js REPL, um uns selbst davon zu überzeugen. Geben Sie in Ihrem Terminal den Befehl node ein:

      Sie sehen, dass die Eingabeaufforderung mit > beginnt.

      Die Funktion alloc() verwendet die Größe des Puffers als erstes und einziges erforderliches Argument. Die Größe ist eine Ganzzahl, die angibt, wie viele Speicherbytes das Pufferobjekt verwendet. Wenn wir beispielsweise einen Puffer mit einer Größe von 1 KB (Kilobyte) erstellen möchten, der 1024 Byte entspricht, geben Sie dies in die Konsole ein:

      • const firstBuf = Buffer.alloc(1024);

      Um einen neuen Puffer zu erstellen, haben wir die global verfügbare Puffer-Klasse verwendet, die über die Methode alloc() verfügt. Durch die Angabe von 1024 als Argument für alloc() haben wir einen Puffer mit einer Größe von 1 KB erstellt.

      Wenn Sie einen Puffer mit alloc() initialisieren, wird der Puffer standardmäßig mit binären Nullen als Platzhalter für festgelegte Datenmengen gefüllt. Wir können dennoch den Standardwert ändern, wenn wir möchten. Wenn wir einen neuen Puffer mit Einsen anstelle von Nullen erstellen möchten, setzen wir den zweiten Parameter der Funktion alloc()fill.

      Erstellen Sie in Ihrem Terminal an der REPL-Eingabeaufforderung einen neuen Puffer, der mit Einsen gefüllt ist:

      • const filledBuf = Buffer.alloc(1024, 1);

      Wir haben gerade ein neues Pufferobjekt erstellt, das auf einen Speicherplatz im Speicher verweist, in dem 1 KB Einsen gespeichert sind. Obwohl wir eine Ganzzahl eingegeben haben, sind alle in einem Puffer gespeicherten Daten Binärdaten.

      Binärdaten können in vielen verschiedenen Formaten vorliegen. Betrachten wir beispielsweise eine Binärsequenz, die ein Datenbyte darstellt: 01110110. Wenn diese Binärsequenz eine Zeichenfolge in Englisch unter Verwendung des ASCII-Codierungsstandards darstellen würde, wäre dies der Buchstabe v. Wenn unser Computer jedoch ein Bild verarbeitet, könnte diese Binärsequenz Informationen über die Farbe eines Pixels enthalten.

      Der Computer kann sie unterschiedlich verarbeiten, da die Bytes unterschiedlich codiert sind. Die Bytecodierung ist das Format des Bytes. Ein Puffer in Node.js verwendet standardmäßig das UTF-8-Codierungsschema, wenn er mit Zeichenfolgendaten initialisiert wird. Ein Byte in UTF-8 repräsentiert eine Zahl, einen Buchstaben (in Englisch und in anderen Sprachen) oder ein Symbol. UTF-8 ist eine Obermenge von ASCII, dem amerikanischen Standardcode für den Informationsaustausch. ASCII kann Bytes mit englischen Groß- und Kleinbuchstaben, den Zahlen 0-9 und einigen anderen Symbolen wie dem Ausrufezeichen (!) codieren oder mit dem kaufmännischen Und-Zeichen (&).

      Wenn wir ein Programm schreiben würden, das nur mit ASCII-Zeichen arbeiten könnte, könnten wir die von unserem Puffer verwendete Codierung mit dem dritten Argument der Funktion alloc() ändern – der Codierung.

      Erstellen wir einen neuen Puffer, der fünf Byte lang ist und nur ASCII-Zeichen speichert:

      • const asciiBuf = Buffer.alloc(5, 'a', 'ascii');

      Der Puffer wird mit fünf Bytes des Zeichens a unter Verwendung der ASCII-Darstellung initialisiert.

      Hinweis: Node.js unterstützt standardmäßig die folgenden Zeichencodierungen:

      • ASCII, dargestellt als ascii
      • UTF-8, dargestellt als utf-8 oder utf8
      • UTF-16, dargestellt als utf-16le oder utf16le
      • UCS-2, dargestellt als ucs-2 oder ucs2
      • Base64, dargestellt als base64
      • Hexadezimal, dargestellt als hex
      • ISO/IEC 8859-1, dargestellt als latin1 oder binär

      Alle diese Werte können in Pufferklassenfunktionen verwendet werden, die einen Codierungsparameter akzeptieren. Daher sind diese Werte alle für die Methode alloc() gültig.

      Bisher haben wir mit der Funktion alloc() neue Puffer erstellt. Manchmal möchten wir jedoch einen Puffer aus bereits vorhandenen Daten erstellen, z. B. einer Zeichenfolge oder einer Anordnung.

      Um einen Puffer aus bereits vorhandenen Daten zu erstellen, verwenden wir die Methode from(). Wir können diese Funktion verwenden, um Puffer zu erstellen aus:

      • Eine Anordnung von Ganzzahlen: die ganzzahligen Werte können zwischen 0 und 255 liegen.
      • Ein ArrayBuffer: Dies ist ein JavaScript-Objekt, das eine festgesetzte Länge von Bytes speichert.
      • Eine Zeichenfolge.
      • Ein weiterer Puffer.
      • Andere JavaScript-Objekte mit einer Symbol.toPrimitive-Eigenschaft. Diese Eigenschaft teilt JavaScript mit, wie das Objekt in einen primitiven Datentyp konvertiert werden soll: Boolescher Wert, Nullwert, undefiniert, Zahl, Zeichenfolge oder Symbol. Weitere Informationen zu Symbolen finden Sie in der JavaScript-Dokumentation von Mozilla.

      Sehen wir uns an, wie wir einen Puffer aus einer Zeichenfolge erstellen können. Geben Sie in der Node.js Folgendes ein:

      • const stringBuf = Buffer.from('My name is Paul');

      Wir haben jetzt ein Pufferobjekt aus der Zeichenfolge My name is Paul​​​ erstellt. Erstellen wir einen neuen Puffer aus einem anderen Puffer, den wir zuvor erstellt haben:

      • const asciiCopy = Buffer.from(asciiBuf);

      Wir haben jetzt einen neuen Puffer asciiCopy erstellt, der dieselben Daten wie asciiBuf enthält.

      Nachdem wir die Erstellung von Puffern erlebt haben, können wir uns mit Beispielen zum Lesen ihrer Daten befassen.

      Schritt 2 – Lesen aus einem Puffer

      Es gibt viele Möglichkeiten, auf Daten in einem Puffer zuzugreifen. Wir können auf ein einzelnes Byte in einem Puffer zugreifen oder den gesamten Inhalt extrahieren.

      Um auf ein Byte eines Puffers zuzugreifen, übergeben wir den Index oder die Position des gewünschten Bytes. Puffer speichern Daten sequentiell wie Arrays. Sie indizieren ihre Daten auch wie Arrays, beginnend bei 0. Wir können die Array-Notation für das Pufferobjekt verwenden, um ein einzelnes Byte zu erhalten.

      Sehen wir uns an, wie dies aussieht, indem Sie einen Puffer aus einer Zeichenfolge in der REPL erstellen:

      • const hiBuf = Buffer.from('Hi!');

      Lesen wir nun das erste Byte des Puffers:

      Wenn Sie ENTER drücken, zeigt die REPL Folgendes:

      Output

      72

      Die Ganzzahl 72 entspricht der UTF-8-Darstellung für den Buchstaben H.

      Hinweis: Die Werte für Bytes können Zahlen zwischen 0 und 255 sein. Ein Byte ist eine Folge von 8 Bits. Ein Bit ist binär und kann daher nur einen von zwei Werten haben: 0 oder 1. Wenn wir eine Folge von 8 Bits und zwei mögliche Werte pro Bit haben, haben wir maximal 2⁸ mögliche Werte für ein Byte. Das führt zu einer maximalen Größe von 256 Werten. Da wir ab Null zählen, bedeutet dies, dass unsere höchste Zahl 255 ist.

      Machen wir dasselbe für das zweite Byte. Geben Sie Folgendes in die REPL ein:

      Die REPL gibt 105 zurück, was den Kleinbuchstaben i darstellt.

      Wir erhalten schließlich das dritte Zeichen:

      In der REPL wird 33 angezeigt, was ! entspricht.

      Versuchen wir, ein Byte aus einem ungültigen Index abzurufen:

      Die REPL wird Folgendes ausgeben:

      Output

      undefined

      Dies ist genau so, als hätten wir versucht, auf ein Element in einer Anordnung mit einem falschen Index zuzugreifen.

      Nachdem wir nun gesehen haben, wie einzelne Bytes eines Puffers gelesen werden, sehen wir uns unsere Optionen zum gleichzeitigen Abrufen aller in einem Puffer gespeicherten Daten an. Das Pufferobjekt wird mit den Methoden toString() und toJSON() bereitgestellt, die den gesamten Inhalt eines Puffers in zwei verschiedenen Formaten zurückgeben.

      Wie der Name schon sagt, konvertiert die toString()-Methode die Bytes des Puffers in eine Zeichenfolge und gibt sie an den Benutzer zurück. Wenn wir diese Methode auf hiBuf verwenden, erhalten wir die Zeichenfolge Hi!. Versuchen wir es!

      Geben Sie in der Eingabeaufforderung Folgendes ein:

      Die REPL wird Folgendes ausgeben:

      Output

      'Hi!'

      Dieser Puffer wurde aus einer Zeichenfolge erstellt. Sehen wir uns an, was passiert, wenn wir toString() in einem Puffer verwenden, der nicht aus Zeichenfolgendaten hergestellt wurde.

      Erstellen wir einen neuen, leeren Puffer, der 10 Bytes groß ist:

      • const tenZeroes = Buffer.alloc(10);

      Verwenden wir nun die toString()-Methode:

      Wir sehen das folgende Ergebnis:

      'u0000u0000u0000u0000u0000u0000u0000u0000u0000u0000'
      

      Die Zeichenfolge u0000 ist das Unicode-Zeichen für NULL. Sie entspricht der Zahl 0. Wenn die Daten des Puffers nicht als Zeichenfolge codiert sind, gibt die Methode toString() die UTF-8-Codierung der Bytes zurück.

      toString() verfügt über einen optionalen Parameter, die Codierung. Mit diesem Parameter können wir die Codierung der zurückgegebenen Pufferdaten ändern.

      Wenn Sie beispielsweise die hexadezimale Codierung für hiBuf wünschen, geben Sie bei der Eingabeaufforderung Folgendes ein:

      Diese Aussage wird folgendermaßen bewertet:

      Output

      '486921'

      486921 ist die hexadezimale Darstellung für die Bytes, die die Zeichenfolge Hi! darstellen. Wenn Benutzer in Node.js die Codierung von Daten von einem Formular in ein anderes konvertieren möchten, legen sie die Zeichenfolge normalerweise in einen Puffer und rufen toString() mit der gewünschten Codierung auf.

      Die toJSON()-Methode verhält sich anders. Unabhängig davon, ob der Puffer aus einer Zeichenfolge erstellt wurde oder nicht, werden die Daten immer als ganzzahlige Darstellung des Bytes zurückgegeben.

      Lassen Sie uns die Puffer hiBuf und tenZeroes erneut verwenden, um die Verwendung von toJSON() zu üben. Geben Sie bei der Eingabeaufforderung Folgendes ein:

      Die REPL wird Folgendes ausgeben:

      Output

      { type: 'Buffer', data: [ 72, 105, 33 ] }

      Das JSON-Objekt verfügt über eine type-Eigenschaft, die immer Puffer ist. Auf diese Weise können Programme diese JSON-Objekte von anderen JSON-Objekten unterscheiden.

      Die Daten-Eigenschaft enthält eine Anordnung der ganzzahligen Darstellung der Bytes. Möglicherweise haben Sie bemerkt, dass 72, 105 und 33 den Werten entsprechen, die wir beim einzelnen Abrufen der Bytes erhalten haben.

      Versuchen wir die toJSON()-Methode mit tenZeroes:

      In der REPL sehen Sie Folgendes:

      Output

      { type: 'Buffer', data: [ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ] }

      Der Typ ist der gleiche wie zuvor angegeben. Die Daten sind jedoch jetzt eine Anordnung mit zehn Nullen.

      Nachdem wir uns nun mit den wichtigsten Möglichkeiten zum Lesen aus einem Puffer befasst haben, schauen wir uns an, wie wir den Inhalt eines Puffers ändern.

      Schritt 3 — Modifizieren eines Puffers

      Es gibt viele Möglichkeiten, um ein bestehendes Pufferobjekt zu ändern. Ähnlich wie beim Lesen können wir Pufferbytes mithilfe der Array-Syntax einzeln ändern. Wir können auch neue Inhalte in einen Puffer schreiben und die vorhandenen Daten ersetzen.

      Lassen Sie uns zunächst untersuchen, wie wir einzelne Bytes eines Puffers ändern können. Erinnern Sie sich an unsere Puffervariable hiBuf, die den String Hi! enthält. Lassen Sie uns jedes Byte so ändern, dass es stattdessen Hey enthält.

      Versuchen wir in der REPL zunächst, das zweite Element von hiBuf auf e zu setzen:

      Lassen Sie uns diesen Puffer nun als Zeichenfolge betrachten, um zu bestätigen, dass die richtigen Daten gespeichert werden. Rufen Sie anschließend die toString()-Methode auf:

      Sie wird bewertet als:

      Output

      'Hu0000!'

      Wir haben diese seltsame Ausgabe erhalten, weil der Puffer nur einen ganzzahligen Wert akzeptieren kann. Wir können ihn nicht dem Buchstaben e zuordnen; vielmehr müssen wir ihm die Zahl zuweisen, deren binäres Äquivalent e darstellt:

      Wenn wir nun die toString()-Methode aufrufen:

      In der REPL erhalten wir diese Ausgabe:

      Output

      'He!'

      Um das letzte Zeichen im Puffer zu ändern, müssen wir das dritte Element auf die Ganzzahl setzen, die dem Byte für y entspricht:

      Lassen Sie uns dies noch einmal mit der toString()-Methode bestätigen:

      Ihre REPL zeigt:

      Output

      'Hey'

      Wenn wir versuchen, ein Byte zu schreiben, das außerhalb des Bereichs des Puffers liegt, wird es ignoriert und der Inhalt des Puffers ändert sich nicht. Versuchen wir beispielsweise, das nicht vorhandene vierte Element des Puffers auf o zu setzen:

      Wir können bestätigen, dass der Puffer mit der toString()-Methode unverändert bleibt:

      Die Ausgabe ist immer noch:

      Output

      'Hey'

      Wenn wir den Inhalt des gesamten Puffers ändern möchten, können wir die write()-Methode verwenden. Die write()-Methode akzeptiert eine Zeichenfolge, die den Inhalt eines Puffers ersetzt.

      Verwenden wir die write()-Methode, um den Inhalt von hiBuf wieder zu Hi! zu ändern. Geben Sie in Ihrer Node.js-Shell bei der Eingabeaufforderung den folgenden Befehl ein:

      Die write()-Methode hat 3 in der REPL zurückgegeben. Dies liegt daran, dass drei Datenbytes geschrieben wurden. Jeder Buchstabe hat eine Byte-Größe, da dieser Puffer eine UTF-8-Codierung verwendet, bei der für jedes Zeichen ein Byte verwendet wird. Wenn der Puffer eine UTF-16-Codierung verwendet hätte, die mindestens zwei Bytes pro Zeichen enthält, hätte die Funktion write() 6 zurückgegeben.

      Überprüfen Sie nun den Inhalt des Puffers mit toString():

      Die REPL wird Folgendes ausgeben:

      Output

      'Hi!'

      Dies ist schneller, als jedes Element byteweise ändern zu müssen.

      Wenn Sie versuchen, mehr Bytes als die Größe eines Puffers zu schreiben, akzeptiert das Pufferobjekt nur, welche Bytes passen. Erstellen wir zur Veranschaulichung einen Puffer, in dem drei Bytes gespeichert sind:

      • const petBuf = Buffer.alloc(3);

      Versuchen wir nun, Cats darauf zu schreiben:

      Wenn der Aufruf von write() ausgewertet wird, gibt die REPL 3 zurück und zeigt an, dass nur drei Bytes in den Puffer geschrieben wurden. Bestätigen Sie nun, dass der Puffer die ersten drei Bytes enthält:

      Die REPL gibt Folgendes aus:

      Output

      'Cat'

      Die Funktion write() fügt die Bytes in sequentieller Reihenfolge hinzu, sodass nur die ersten drei Bytes in den Puffer gestellt wurden.

      Im Gegensatz dazu erstellen wir einen Puffer, der vier Bytes speichert:

      • const petBuf2 = Buffer.alloc(4);

      Schreiben Sie den gleichen Inhalt darauf:

      Fügen Sie dann einige neue Inhalte hinzu, die weniger Platz beanspruchen als der ursprüngliche Inhalt:

      Da Puffer nacheinander ab 0 schreiben, wenn wir den Inhalt des Puffers drucken:

      Wir würden begrüßt werden mit:

      Output

      'Hits'

      Die ersten beiden Zeichen werden überschrieben, der Rest des Puffers bleibt jedoch unberührt.

      Manchmal befinden sich die Daten, die wir in unserem bereits vorhandenen Puffer haben möchten, nicht in einer Zeichenfolge, sondern in einem anderen Pufferobjekt. In diesen Fällen können wir die copy()-Funktion verwenden, um zu ändern, was unser Puffer speichert.

      Erstellen wir zwei neue Puffer:

      • const wordsBuf = Buffer.from('Banana Nananana');
      • const catchphraseBuf = Buffer.from('Not sure Turtle!');

      Die Puffer wordsBuf und catchphraseBuf enthalten beide Zeichenfolgendaten. Wir möchten catchphraseBuf so ändern, dass es Nananana Turtle! speichert statt Not sure Turtle!​​​. Wir werden copy() verwenden, um Nananana von wordsBuf zu catchphraseBuf zu bekommen.

      Um Daten von einem Puffer in den anderen zu kopieren, verwenden wir die copy()-Methode für den Puffer, der die Informationsquelle darstellt. Da wordsBuf die zu kopierenden Zeichenfolgendaten enthält, müssen wir diese wie folgt kopieren:

      • wordsBuf.copy(catchphraseBuf);

      Der Zielparameter ist in diesem Fall der catchphraseBuf-Puffer.

      Wenn wir das in die REPL eingeben, gibt es 15 zurück, was anzeigt, dass 15 Bytes geschrieben wurden. Die Zeichenfolge Nananana verwendet nur 8 Datenbytes, sodass wir sofort wissen, dass unsere Kopie nicht wie beabsichtigt verlaufen ist. Verwenden Sie die toString()-Methode, um den Inhalt von catchphraseBuf anzuzeigen:

      • catchphraseBuf.toString();

      Die REPL gibt Folgendes aus:

      Output

      'Banana Nananana!'

      Standardmäßig hat copy() den gesamten Inhalt von wordsBuf übernommen und in catchphraseBuf abgelegt. Wir müssen selektiver für unser Ziel sein und nur Nananana kopieren. Lassen Sie uns den ursprünglichen Inhalt von catchphraseBuf neu schreiben, bevor wir fortfahren:

      • catchphraseBuf.write('Not sure Turtle!');

      Die Funktion copy() verfügt über einige weitere Parameter, mit denen wir anpassen können, welche Daten in den anderen Puffer kopiert werden. Hier ist eine Liste aller Parameter dieser Funktion:

      • target – Dies ist der einzige erforderliche Parameter von copy(). Wie wir aus unserer vorherigen Verwendung gesehen haben, ist dies der Puffer, in den wir kopieren möchten.
      • targetStart – Dies ist der Index der Bytes im Zielpuffer, in den mit dem Kopieren begonnen werden soll. Standardmäßig ist es 0, d. h. es werden Daten kopiert, die am Anfang des Puffers beginnen.
      • sourceStart – Dies ist der Index der Bytes im Quellpuffer, aus denen kopiert werden soll.
      • sourceEnd – Dies ist der Index der Bytes im Quellpuffer, in den das Kopieren beendet werden soll. Standardmäßig ist es die Länge des Puffers.

      Um Nananana aus wordsBuf in catchphraseBuf zu kopieren, sollte unser Ziel catchphraseBuf wie zuvor sein. Der targetStart wäre 0, da Nananana am Anfang von catchphraseBuf erscheinen soll. Der sourceStart sollte 7 sein, da dies der Index ist, in dem Nananana in wordsBuf beginnt. Das sourceEnd würde weiterhin die Länge der Puffer sein.

      Kopieren Sie bei der REPL-Eingabeaufforderung den Inhalt von wordsBuf folgendermaßen:

      • wordsBuf.copy(catchphraseBuf, 0, 7, wordsBuf.length);

      Die REPL bestätigt, dass 8 Bytes geschrieben wurden. Beachten Sie, wie wordsBuf.length als Wert für den Parameter sourceEnd verwendet wird. Wie bei Arrays gibt uns die Eigenschaft length die Größe des Puffers an.

      Nun sehen wir uns den Inhalt von catchphraseBuf an:

      • catchphraseBuf.toString();

      Die REPL gibt Folgendes aus:

      Output

      'Nananana Turtle!'

      Erfolg! Wir konnten die Daten von catchphraseBuf ändern, indem wir den Inhalt von wordsBuf kopiert haben.

      Sie können die Node.js REPL beenden, wenn Sie dies möchten. Beachten Sie, dass alle erstellten Variablen nicht mehr verfügbar sind, wenn Sie Folgendes tun:

      Zusammenfassung

      In diesem Tutorial haben Sie gelernt, dass Puffer Zuordnungen fester Länge im Speicher sind, in denen Binärdaten gespeichert werden. Sie haben zuerst Puffer erstellt, indem Sie ihre Größe im Speicher definiert und sie mit bereits vorhandenen Daten initialisiert haben. Anschließend lasen Sie Daten aus einem Puffer, indem Sie die einzelnen Bytes untersucht und die Methoden toString() und toJSON() verwendet haben. Schließlich haben Sie die von einem Puffer gespeicherten Daten geändert, indem Sie die einzelnen Bytes geändert und die Methoden write() und copy() verwendet haben.

      Puffer geben Ihnen einen guten Einblick, wie Binärdaten von Node.js manipuliert werden. Jetzt, da Sie mit Puffern interagieren können, können Sie die verschiedenen Auswirkungen der Zeichenkodierung auf die Speicherung von Daten beobachten. Sie können beispielsweise Puffer aus Zeichenfolgendaten erstellen, die keine UTF-8- oder ASCII-Codierung aufweisen, und deren Größenunterschied beobachten. Sie können auch einen Puffer mit UTF-8 und toString() verwenden, um ihn in andere Codierungsschemata zu konvertieren.

      Informationen zu Puffern in Node.js finden Sie in der Node.js-Dokumentation zum Puffer-Objekt. Wenn Sie Node.js weiter lernen möchten, können Sie zur Codieren in Node-Reihe zurückkehren oder Programmierprojekte und -einstellungen auf unserer Node-Themenseite durchsuchen.



      Source link

      Cómo agregar espacio de intercambio en Ubuntu 20.04


      Introducción

      Una opción para evitar errores de memoria insuficiente en aplicaciones es agregar espacio de intercambio a su servidor. En esta guía, abordaremos la manera de añadir un archivo de intercambio a un servidor Ubuntu 20.04.

      Advertencia: Si bien, en general, el intercambio se recomienda para sistemas que utilizan discos duros giratorios tradicionales, utilizarlo con SSD puede causar problemas de degradación de hardware con el paso del tiempo. Debido a esta consideración, no recomendamos habilitar el intercambio en DigitalOcean ni en ningún otro proveedor que utilice almacenamiento SSD.

      ¿Qué es el espacio de intercambio?

      El Swap es una porción de almacenamiento de las unidades de disco duro que se reserva para el sistema operativo, a fin de almacenar de forma temporal datos que ya no es posible contener en la memoria RAM. Esto le permite aumentar la cantidad de información que su servidor puede conservar en su memoria funcional, con algunas advertencias. El espacio de intercambio en el disco duro se utilizará principalmente cuando ya no haya espacio suficiente en la RAM para mantener datos de aplicación en uso.

      La información escrita en el disco será considerablemente más lenta que la información conservada en la RAM, aunque el sistema operativo preferiría seguir ejecutando datos de aplicación en la memoria y utilizar el espacio de intercambio para los datos antiguos. En general, disponer de espacio de intercambio como una segunda opción para cuando la RAM de su sistema se agote puede funcionar como una buena red de seguridad contra excepciones por falta de memoria en sistemas con almacenamiento no SSD disponible.

      Paso 1: Comprobar la información de intercambio del sistema

      Antes de empezar, podemos verificar si el sistema ya cuenta con espacio de intercambio disponible. Es posible contar con una variedad de archivos o particiones de intercambio, pero generalmente debería bastar con uno de ellos.

      Podemos ver si el sistema cuenta con algún intercambio configurado ingresando lo siguiente:

      Si no obtiene ningún resultado, esto significa que su sistema no tiene espacio de intercambio disponible actualmente.

      Puede verificar que no haya intercambio activo con la utilidad free:

      Output

      total used free shared buff/cache available Mem: 981Mi 122Mi 647Mi 0.0Ki 211Mi 714Mi Swap: 0B 0B 0B

      Como puede ver en la fila de Swap de los resultados, no existe intercambio activo en el sistema.

      Paso 2: Comprobar el espacio disponible en la partición de la unidad de disco duro

      Antes de crear nuestro archivo de intercambio, comprobaremos la utilización actual de nuestro disco para asegurarnos de contar con espacio suficiente. Realice esto ingresando lo siguiente:

      Output

      Filesystem Size Used Avail Use% Mounted on udev 474M 0 474M 0% /dev tmpfs 99M 932K 98M 1% /run /dev/vda1 25G 1.4G 23G 7% / tmpfs 491M 0 491M 0% /dev/shm tmpfs 5.0M 0 5.0M 0% /run/lock tmpfs 491M 0 491M 0% /sys/fs/cgroup /dev/vda15 105M 3.9M 101M 4% /boot/efi /dev/loop0 55M 55M 0 100% /snap/core18/1705 /dev/loop1 69M 69M 0 100% /snap/lxd/14804 /dev/loop2 28M 28M 0 100% /snap/snapd/7264 tmpfs 99M 0 99M 0% /run/user/1000

      El dispositivo con / en la columna Mounted on es nuestro disco en este caso. En este ejemplo, contamos con bastante espacio disponible (solo 1.4 GB utilizados). Probablemente su uso será diferente.

      Aunque existen varias opiniones acerca de la dimensión adecuada de un espacio de intercambio, depende de sus preferencias personales y de los requisitos de su aplicación. En general, una cantidad idéntica o equivalente al doble de la cantidad de RAM en su sistema es un buen punto de partida. Otra buena regla general es que cualquier cosa que supere los 4G de intercambio es probablemente innecesaria si lo está utilizando como una segunda opción al RAM.

      Paso 3: Creación de un archivo de intercambio

      Ahora que conocemos nuestro espacio de disco duro disponible, podemos crear un archivo de intercambio en nuestro sistema de archivos. Asignaremos un archivo del tamaño que deseemos y lo llamaremos swapfile en nuestro directorio root (/).

      La mejor opción para crear un archivo de intercambio es usar el programa fallocate. Este comando crea instantáneamente un archivo del tamaño especificado.

      Dado que el servidor de nuestro ejemplo tiene 1 G de RAM, crearemos un archivo de 1 G en esta guía. Ajuste esto para satisfacer las necesidades de su propio servidor:

      • sudo fallocate -l 1G /swapfile

      Podemos verificar que la cantidad correcta de espacio estaba reservada ingresando lo siguiente:

      • -rw-r--r-- 1 root root 1.0G Apr 25 11:14 /swapfile

      Nuestro archivo se ha creado con la cantidad correcta de espacio reservado.

      Paso 4: Habilitación del archivo de intercambio

      Ahora que tenemos un archivo del tamaño correcto disponible, debemos convertir esto en espacio de intercambio.

      Primero, debemos restringir los permisos del archivo para que solo los usuarios con privilegios de root puedan leer el contenido. Esto impide que usuarios comunes puedan acceder al archivo, lo que tendría implicaciones de seguridad significativas.

      Ingrese lo siguiente para que solo root pueda acceder al archivo:

      Verifique el cambio de permisos introduciendo lo siguiente:

      Output

      -rw------- 1 root root 1.0G Apr 25 11:14 /swapfile

      Como puede ver, los indicadores de lectura y escritura solo están habilitados para el root user.

      Ahora podemos marcar el archivo como espacio de intercambio al ingresar lo siguiente:

      Output

      Setting up swapspace version 1, size = 1024 MiB (1073737728 bytes) no label, UUID=6e965805-2ab9-450f-aed6-577e74089dbf

      Después de marcar el archivo, podemos habilitar el archivo de intercambio, lo cual permite que nuestro sistema empiece a utilizarlo:

      Verifique que el intercambio esté disponible ingresando lo siguiente:

      Output

      NAME TYPE SIZE USED PRIO /swapfile file 1024M 0B -2

      Podemos verificar los resultados de la utilidad free de nuevo para corroborar nuestros hallazgos:

      Output

      total used free shared buff/cache available Mem: 981Mi 123Mi 644Mi 0.0Ki 213Mi 714Mi Swap: 1.0Gi 0B 1.0Gi

      Nuestro intercambio se ha configurado con éxito y nuestro sistema operativo comenzará a utilizarlo según sea necesario.

      Paso 5: Lograr que el archivo de intercambio sea permanente

      Nuestros cambios recientes habilitaron el archivo de intercambio para la sesión en curso. Sin embargo, si reiniciamos, el servidor no conservará los ajustes de intercambio de forma automática. Podemos cambiar esto añadiendo el archivo de intercambio a nuestro archivo /etc/fstab.

      Respalde el archivo /etc/fstab en caso de que algún imprevisto:

      • sudo cp /etc/fstab /etc/fstab.bak

      Añada la información del archivo de intercambio al final de su archivo /etc/fstab ingresando lo siguiente:

      • echo '/swapfile none swap sw 0 0' | sudo tee -a /etc/fstab

      A continuación, revisaremos algunos ajustes que podamos actualizar para regular nuestro espacio de intercambio.

      Paso 6: Ajustar sus ajustes de intercambio

      Existen algunas opciones que puede configurar y que tendrán efecto en el rendimiento de su sistema al gestionar un intercambio.

      Ajuste de la propiedad “”swappiness”

      El parámetro swappiness configura la frecuencia con la cual su sistema intercambia datos de la RAM al espacio de intercambio. Este es un valor entre 0 y 100 que representa un porcentaje.

      Con valores cercanos a cero, el núcleo no intercambiará datos en el disco a menos que sea absolutamente necesario. Recuerde que las interacciones con el archivo de intercambio son “exigentes” puesto que tardan mucho más que las interacciones con la RAM y pueden reducir el rendimiento considerablemente. Indicar al sistema que no dependa del intercambio en demasía hará que sea más rápido.

      En un esfuerzo por mantener más espacio de RAM libre, se intentará verter al intercambio más datos de los valores cercanos a 100. Dependiendo del perfil de memoria de sus aplicaciones o para qué está utilizando su servidor, esto podría ser mejor en algunos casos.

      Podemos ver el valor de intercambiabilidad actual al ingresar lo siguiente:

      • cat /proc/sys/vm/swappiness

      Output

      60

      Para un escritorio, un ajuste de intercambiabilidad de 60 no es un mal valor. Para un servidor, posiblemente le convenga acercarlo a 0.

      Podemos fijar la capacidad de intercambio en un valor diferente con el comando sysctl.

      Por ejemplo, para establecer la intercambiabilidad en 10, podríamos ingresar lo siguiente:

      • sudo sysctl vm.swappiness=10

      Output

      vm.swappiness = 10

      Esta configuración persistirá hasta el próximo reinicio. Podemos establecer este valor automáticamente en el reinicio añadiendo la línea a nuestro archivo /etc/sysctl.conf:

      • sudo nano /etc/sysctl.conf

      En la parte inferior, puede añadir:

      /etc/sysctl.conf

      vm.swappiness=10
      

      Guarde y cierre el archivo cuando haya terminado.

      Ajustar la configuración de presión de caché

      Otro valor relacionado que podría querer modificar es el vfs_cache_pressure. Esta ajuste determina en qué medida el sistema elegirá almacenar en caché información de inodos y entradas de directorio en lugar de otros datos.

      Básicamente, estos son datos de acceso sobre el sistema de archivos. Generalmente, la búsqueda de esto supone costos muy altos y se solicita con mucha frecuencia, por lo cual el almacenamiento en caché . Puede ver el valor actual consultando el sistema de archivos proc nuevamente:

      • cat /proc/sys/vm/vfs_cache_pressure

      Output

      100

      Dado que está configurado actualmente, nuestro sistema elimina la información de inodo de la memoria caché demasiado rápido. Podemos establecer esto en un parámetro más conservador como 50 al ingresar:

      • sudo sysctl vm.vfs_cache_pressure=50

      Output

      vm.vfs_cache_pressure = 50

      Una vez más, esto solo es válido para nuestra sesión actual. Podemos cambiar eso añadiéndolo en nuestro archivo de configuración como hicimos con nuestro ajuste de intercambiabilidad:

      • sudo nano /etc/sysctl.conf

      En la parte inferior, añada la línea que especifique su nuevo valor:

      /etc/sysctl.conf

      vm.vfs_cache_pressure=50
      

      Guarde y cierre el archivo cuando haya terminado.

      Conclusión

      Seguir los pasos de esta guía le brindará un respiro en casos en los que podrían generarse excepciones por falta de memoria. El espacio de intercambio puede ser increíblemente útil para evitar algunos de estos problemas comunes.

      Si encuentra errores de falta de memoria (OOM) u observa que su sistema no puede utilizar las aplicaciones que necesita, la mejor solución es optimizar sus configuraciones de aplicaciones o actualizar su servidor.



      Source link

      Cómo agregar prueba de unidades a su proyecto de Django


      El autor seleccionó a Open Internet/Free Speech Fund para recibir una donación como parte del programa Write for DOnations.

      Introducción

      Es casi imposible crear sitios web que desde el principio funcionen a la perfección y no tengan errores. Por ese motivo, debe probar su aplicación web para encontrar esos errores y trabajar en ellos de forma proactiva. Para mejorar la eficiencia de las pruebas, es común desglosarlas en unidades que prueben funcionalidades específicas de la aplicación web. Esta práctica se denomina prueba de unidades. Hace que sea más fácil detectar errores, dado que las pruebas se centran en pequeñas partes (unidades) de su proyecto independientemente de otras.

      Probar un sitio web puede ser una tarea compleja, ya que consta de varias capas de lógica, como la gestión de solicitudes HTTP, la validación de formularios y la representación de plantillas. Sin embargo, Django ofrece un conjunto de herramientas que le permite probar su aplicación web sin problemas. Aunque es posible utilizar otros marcos de pruebas, la opción preferida para escribir pruebas en Django es usar el módulo unittest de Python.

      A través de este tutorial, configurará un conjunto de pruebas en su proyecto de Django y escribirá pruebas de unidades para los modelos y vistas de su aplicación. Ejecutará estas pruebas, analizará sus resultados y aprenderá a encontrar las causas de errores en las pruebas que fallan.

      Requisitos previos

      Antes de iniciar este tutorial, necesitará lo siguiente:

      Paso 1: Agregar un conjunto de pruebas a su aplicación en Django

      Un conjunto de pruebas en Django es una colección de todos los casos de prueba en todas las aplicaciones de su proyecto. Para lograr que la utilidad de prueba de Django descubra los casos de prueba que tiene, se escriben los casos de prueba en secuencias de comandos cuyos nombres comiencen con test. En este paso, creará la estructura del directorio y los archivos para su conjunto de pruebas, y creará un caso de prueba vacío dentro de él.

      Si siguió la serie de tutoriales Desarrollo en Django, tendrá una aplicación de Django denominada blogsite.

      Crearemos una carpeta para contener todos nuestros scripts de prueba. Primero, active el entorno virtual:

      • cd ~/my_blog_app
      • . env/bin/activate

      Luego, diríjase al directorio de la aplicación blogsite, la carpeta que contiene los archivos models.py y views.py, y luego cree una carpeta nueva llamada tests:

      • cd ~/my_blog_app/blog/blogsite
      • mkdir tests

      A continuación, convertirá esta carpeta en un paquete Python; agregue un archivo __init__.py:

      • cd ~/my_blog_app/blog/blogsite/tests
      • touch __init__.py

      Ahora, agregue un archivo para probar sus modelos y otro para probar sus vistas:

      • touch test_models.py
      • touch test_views.py

      Por último, creará un caso de prueba vacío en test_models.py. Deberá importar la clase TestCase de Django y hacer que sea una súper clase de su propia clase de caso de prueba. Más adelante, añadirá métodos a este caso de prueba para probar la lógica en sus modelos. Abra el archivo test_models.py​​​1​​​:

      Luego, añada el siguiente código al archivo:

      ~/my_blog_app/blog/blogsite/tests/test_models.py

      from django.test import TestCase
      
      class ModelsTestCase(TestCase):
          pass
      

      De esta manera, habrá añadido correctamente un conjunto de pruebas a la aplicación blogsite. Luego, completará los detalles del caso de prueba de modelo vacío que creó aquí.

      Paso 2: Probar su código Python

      En este paso, probará la lógica del código escrito en el archivo models.py. En particular, probará el método save del modelo Post para asegurarse de que cree el slug correcto del título de una entrada cuando se invoque.

      En este caso, comenzaremos observando el código que ya tiene en su archivo models.py para ver el método save del modelo Post:

      • cd ~/my_blog_app/blog/blogsite
      • nano models.py

      Observará lo siguiente:

      ~/my_blog_app/blog/blogsite/models.py

      class Post(models.Model):
          ...
          def save(self, *args, **kwargs):
              if not self.slug:
                  self.slug = slugify(self.title)
              super(Post, self).save(*args, **kwargs)
          ...
      

      Podemos ver que verifica si la entrada que está a punto de guardar tiene un valor slug, y, si no, invoca a slugify a fin de crear un valor slug para la misma. Este es el tipo de lógica que tal vez quiera probar para asegurarse de que los slugs se creen en realidad cuando se guarde una entrada.

      Cierre el archivo.

      Para probar esto, regrese a test_models.py:

      Luego, actualícelo para que se vea como a continuación y agregue las porciones resaltadas:

      ~/my_blog_app/blog/blogsite/tests/test_models.py

      from django.test import TestCase
      from django.template.defaultfilters import slugify
      from blogsite.models import Post
      
      
      class ModelsTestCase(TestCase):
          def test_post_has_slug(self):
              """Posts are given slugs correctly when saving"""
              post = Post.objects.create(title="My first post")
      
              post.author = "John Doe"
              post.save()
              self.assertEqual(post.slug, slugify(post.title))
      

      Este nuevo método test_post_has_slug crea una nueva entrada con el título "My first post" y, luego, atribuye a esta un autor y la guarda. Después de esto, usando el método assertEqual del módulo unittest de Python, se comprueba si el slug de la entrada es correcto. El método assertEqual verifica si los dos argumentos que se le pasan son iguales, como lo determina el operador "==", y genera un error si no lo son.

      Guarde y cierre test_models.py.

      Este es un ejemplo de lo que se puede probar. Cuanta más lógica añada a su proyecto, habrá más por probar. Si añade más lógica al método save o crea nuevos métodos para el modelo Post, querrá añadir más pruebas aquí. Usted puede añadirlas al método test_post_has_slug o crear nuevos métodos de prueba, pero sus nombres deben comenzar con test.

      Creó con éxito un caso de prueba para el modelo Post, en el que validó que los slugs se crean correctamente después del guardado. En el siguiente paso, escribirá un caso de prueba para probar las vistas.

      Paso 3: Usar el cliente de prueba de Django

      En este paso, escribirá un caso de prueba que probará una vista utilizando el cliente de prueba de Django. El cliente de prueba es una clase de Python que funciona como navegador web ficticio, le permite probar sus vistas e interactuar con su aplicación Django como un usuario lo haría. Puede acceder al cliente de prueba haciendo referencia a self.client en sus métodos de prueba. Por ejemplo, crearemos un caso de prueba en test_views.py. Primero, abra el archivo test_views.py:

      Luego, agregue lo siguiente:

      ~/my_blog_app/blog/blogsite/tests/test_views.py

      from django.test import TestCase
      
      
      class ViewsTestCase(TestCase):
          def test_index_loads_properly(self):
              """The index page loads properly"""
              response = self.client.get('your_server_ip:8000')
              self.assertEqual(response.status_code, 200)
      

      El ViewsTestCase contiene un método test_index_loads_properly que utiliza el cliente de prueba de Django para visitar la página principal del sitio web (http://your_server_ip:8000, donde your_server_ip es la dirección IP del servidor que utiliza). Luego, el método de prueba comprueba si la respuesta tiene un código de estado 200, lo cual significa que la página respondió sin errores. Como resultado, puede estar seguro de que cuando el usuario visite la página también responderá sin errores.

      Además del código de estado, puede leer sobre otras propiedades de la respuesta del cliente de prueba que puede probar en la página de documentación de respuestas de prueba de Django.

      En este paso, creó un caso de prueba para probar la vista que genera la página principal y verificar que funcione sin errores. Ahora, hay dos casos de prueba en su conjunto de pruebas. En el siguiente paso, los ejecutará para ver sus resultados.

      Paso 4: Ejecutar sus pruebas

      Ahora que terminó de crear un conjunto de pruebas para el proyecto, es el momento de ejecutar estas pruebas y ver sus resultados. Para ejecutar las pruebas, diríjase a la carpeta blog (que contiene el archivo manage.py de la aplicación):

      Luego, ejecútelas con lo siguiente:

      Verá un resultado similar al siguiente en su terminal:

      Output

      Creating test database for alias 'default'... System check identified no issues (0 silenced). .. ---------------------------------------------------------------------- Ran 2 tests in 0.007s OK Destroying test database for alias 'default'...

      En este resultado, hay dos puntos .., cada uno de los cuales representa un caso de prueba aprobado. Ahora, modificará test_views.py para activar una prueba que falle. Abra el archivo con lo siguiente:

      Luego, cambie el código resaltado para ver lo siguiente:

      ~/my_blog_app/blog/blogsite/tests/test_views.py

      from django.test import TestCase
      
      
      class ViewsTestCase(TestCase):
          def test_index_loads_properly(self):
              """The index page loads properly"""
              response = self.client.get('your_server_ip:8000')
              self.assertEqual(response.status_code, 404)
      

      Aquí, cambió el código de estado de 200 a 404. Ahora, ejecute la prueba de nuevo desde su directorio con manage.py:

      Verá el siguiente resultado:

      Output

      Creating test database for alias 'default'... System check identified no issues (0 silenced). .F ====================================================================== FAIL: test_index_loads_properly (blogsite.tests.test_views.ViewsTestCase) The index page loads properly ---------------------------------------------------------------------- Traceback (most recent call last): File "~/my_blog_app/blog/blogsite/tests/test_views.py", line 8, in test_index_loads_properly self.assertEqual(response.status_code, 404) AssertionError: 200 != 404 ---------------------------------------------------------------------- Ran 2 tests in 0.007s FAILED (failures=1) Destroying test database for alias 'default'...

      Verá aparecer un mensaje de error descriptivo en el que se indicará la secuencia de comandos, el caso de prueba y el método que falló. También se mostrará la causa de la falla, con un código de estado que no es igual al 404 en este caso, con el mensaje AssertionError: 200 ! = 404. El AssertionError aquí se genera en la línea de código resaltada en el archivo test_views.py:

      ~/my_blog_app/blog/blogsite/tests/test_views.py

      from django.test import TestCase
      
      
      class ViewsTestCase(TestCase):
          def test_index_loads_properly(self):
              """The index page loads properly"""
              response = self.client.get('your_server_ip:8000')
              self.assertEqual(response.status_code, 404)
      

      Le indica que la afirmación es falsa, es decir, el código de estado de respuesta (200) no es lo que se esperaba (404). Precediendo el mensaje de error, puede ver que los dos puntos .. pasaron a ser . F, lo cual indica que el primer caso de prueba se aprobó mientras que el segundo no.

      Conclusión

      A través de este tutorial, creó un conjunto de pruebas en su proyecto de Django, añadió casos de prueba para probar el modelo y la lógica de visualización, aprendió a ejecutar pruebas y analizó el resultado de una prueba. Como siguiente paso, puede crear nuevas secuencias de comandos para código Python que no estén en models.py y views.py.

      A continuación, consulte algunos artículos que pueden ser útiles al crear y probar sitios web con Django:

      También puede consultar nuestra página de temas de Django para ver tutoriales y proyectos adicionales.



      Source link