Introducci贸n a las transmisiones de Node.js

    Introducci贸n

    Las transmisiones son un concepto algo avanzado de entender. Entonces, en este art铆culo, seguiremos con algunos ejemplos para una mejor comprensi贸n y le presentaremos algunos conceptos en el camino.

    驴Qu茅 es una corriente?

    En t茅rminos simples, los flujos se utilizan para leer desde la entrada o escribir en la salida de forma secuencial. La mayor铆a de las veces, las transmisiones se utilizan para leer o escribir desde una fuente continua o comparativamente grande.

    Por ejemplo, supongamos que tiene que leer un archivo grande. Si el tama帽o del archivo es mayor que su espacio de memoria libre, no puede leer el archivo completo en la memoria para procesarlo. Tienes que leerlo pieza por pieza y procesar cada fragmento, que puede estar separado por una l铆nea, por ejemplo.

    Otro ejemplo de una fuente continua es la comunicaci贸n en red, como una aplicaci贸n de chat donde los datos deben fluir continuamente del remitente al receptor.

    Secuencias en Node.js

    los Stream module es un m贸dulo nativo que se env铆a de forma predeterminada en Node.js. los Stream es una instancia del EventEmitter class, que maneja eventos de forma asincr贸nica en Node.js. Debido a su superclase, las transmisiones se basan inherentemente en eventos.

    Hay 4 tipos de transmisiones en Node.js:

    • Escribible: Se usa para escribir datos secuencialmente
    • Legible: Se usa para leer datos secuencialmente
    • D煤plex: Se utiliza para leer y escribir datos secuencialmente
    • Transformar: Donde los datos se pueden modificar al escribir o leer. Tomemos la compresi贸n como ejemplo, con un flujo como este, puede escribir datos comprimidos y leer datos descomprimidos.

    Echemos un vistazo a algunos ejemplos de transmisiones.

    Secuencias grabables

    En primer lugar, creemos un flujo de escritura y escribamos algunos datos en un archivo:

    const fs = require('fs');
    const file = fs.createWriteStream('file.txt');
    
    file.write('hello world');
    file.end(', from streams!');
    

    En este c贸digo, hemos utilizado el m贸dulo del sistema de archivos para crear una secuencia de escritura en un archivo (file.txt) y escribirle 2 fragmentos separados: hello world y , from streams.

    A diferencia del fs.writeFile() donde necesitamos escribir el contenido del archivo a la vez, usando una secuencia podemos escribir el contenido fragmento por fragmento.

    Para simular una entrada continua, podr铆amos hacer algo como:

    const fs = require('fs');
    const file = fs.createWriteStream('file.txt');
    
    for (let i = 0; i < 10000; i++) {
        file.write('Hello world ' + i);
    }
    file.end();
    

    Esto escribir谩 Hello world + {i} diez mil veces y luego terminar la corriente:

    Hello world 0
    Hello world 1
    Hello world 2
    Hello world 3
    Hello world 4
    ...
    

    Por favor reString .end() sus transmisiones una vez que haya terminado de usarlas, ya que finish El evento se env铆a despu茅s del .end() se ha llamado al m茅todo.

    Esto significa que el cuerpo de la secuencia se ha vaciado en nuestro archivo.

    Secuencias legibles

    Ahora echemos un vistazo a otro ejemplo simple leyendo un archivo usando una secuencia. Podemos leer un archivo fragmento por fragmento, en lugar de leer el contenido completo en la memoria, utilizando una secuencia legible:

    const fs = require('fs');
    
    const readableStream = fs.createReadStream('./article.md', {
        highWaterMark: 10
    });
    
    readableStream.on('readable', () => {
        process.stdout.write(`[${readableStream.read()}]`);
    });
    
    readableStream.on('end', () => {
        console.log('DONE');
    });
    

    De manera similar a la creaci贸n de una secuencia de escritura, hemos creado una secuencia legible llamando al .createReadStream() m茅todo.

    Mientras se almacena en b煤fer (segmentando los datos en fragmentos), el tama帽o del b煤fer depende de la highWaterMark par谩metro, que se pasa al constructor de la secuencia.

    El valor predeterminado de este par谩metro es 16384 bytes (16 kb), por lo que si no anula el par谩metro, la secuencia leer谩 fragmentos de 16 kb y se los pasar谩 para que los procese.

    Dado que estamos usando un archivo de texto peque帽o, tiene m谩s sentido usar un valor peque帽o para nuestro ejemplo, por lo que el texto tendr谩 10 caracteres.

    En nuestro ejemplo anterior, simplemente imprimimos el fragmento de datos que recibimos, excepto con corchetes alrededor para que pueda ver f谩cilmente los diferentes fragmentos. La salida de nuestro c贸digo se ve as铆:

    [### Introd][uction
    
    St][reams are ][a somewhat][ advanced ][concept to][ understan][d. So in t][his articl][e, we will][ go along ][with some ][examples f][or a bette][r understa][nding and ][introduce ][you to a f][ew concept][s along th][e way.
    
    ##][# What is ][a Stream
    
    ][In simple ]...
    

    Secuencias d煤plex

    Con los flujos de escritura y legibles fuera del camino, podemos saltar a un ejemplo usando flujos d煤plex, que esencialmente combinan ambos.

    Los demostraremos usando un servidor HTTP simple construido con el nativo de Node.js http m贸dulo. El ejemplo utilizado aqu铆 es del oficial Documentaci贸n de Node.js.

    Dado que los servidores reciben solicitudes y luego env铆an respuestas, son un buen ejemplo de transmisiones d煤plex, que manejan ambos: una transmisi贸n legible actuar谩 como una solicitud continua y una transmisi贸n de escritura actuar谩 como respuesta.

    Primero, importemos el m贸dulo HTTP:

    const http = require('http');
    

    Ahora creemos un servidor HTTP simple:

    const server = http.createServer((req, res) => {
        // `req` is an http.IncomingMessage, which is a Readable Stream.
        // `res` is an http.ServerResponse, which is a Writable Stream.
    
        let body = '';
    
        // Get the data as utf8 strings.
        // If an encoding is not set, Buffer objects will be received.
        req.setEncoding('utf8');
    
        // Readable streams emit 'data' events once a listener is added.
        req.on('data', (chunk) => {
            body += chunk;
        });
    
        // The 'end' event indicates that the entire body has been received.
        req.on('end', () => {
            consol.log(body);
    
            try {
                // Send 'Hello World' to the user
                res.write('Hello World');
                res.end();
            } catch (er) {
                res.statusCode = 400;
                return res.end(`error: ${er.message}`);
            }
        });
    });
    

    los req El par谩metro es un flujo legible, que procesaremos al recibirlo como una solicitud HTTP. Luego enviaremos res como respuesta, que es, de nuevo, un simple flujo de escritura.

    Luego, usando el .on() m茅todo, leemos el cuerpo de la solicitud en trozos de 64 KB y lo almacenamos en el body, provocado por el data evento.

    Tenga en cuenta el uso del setEncoding() antes de leer de la secuencia.

    De esta forma, la secuencia emitir谩 cadenas y emitir谩 Buffer objetos de otra manera. Sin embargo, tambi茅n puede realizar esa conversaci贸n dentro del data devoluci贸n de llamada de evento si lo prefiere.

    los end El evento se activa cuando no queda nada para leer en una secuencia legible. Hablaremos de otros eventos 煤tiles m谩s adelante en este art铆culo.

    Ahora, escuchemos al servidor:

    server.listen(1337);
    

    Golpeando http://localhost:1337, deber铆as ver un simple Hello World respuesta del servidor HTTP.

    Canalizaciones de flujo

    Mediante el uso de canales de flujo, podemos canalizar directamente flujos legibles a un flujo de escritura sin almacenar el b煤fer temporalmente, por lo que podemos ahorrar espacio en la memoria.

    Considere un escenario en el que un usuario solicita un archivo grande del servidor y no hay espacio de memoria para cargarlo en la memoria, o miles de clientes diferentes solicitan el mismo archivo. En este caso, no podemos leer el contenido del archivo en la memoria y luego volver a escribirlo en el cliente.

    Aqu铆 es donde el pipe El m茅todo es 煤til, ya que canalizaremos un flujo legible (una solicitud) en un flujo de escritura (una respuesta) y se lo entregaremos al usuario sin retenerlo en el b煤fer.

    Primero, hagamos esto sin usar transmisiones:

    const fs = require('fs');
    const server = require('http').createServer();
    
    server.on('request', (req, res) => {
        fs.readFile('./video.mkv', (err, data) => {
            if (err) throw err;
    
            res.end(data);
        });
    });
    
    server.listen(1337);
    

    Este m茅todo es leer directamente el archivo en la memoria usando el .readFile() m茅todo y lo env铆a al usuario.

    Abra su navegador web y vaya a http://localhost:1337, esto es lo que sucede detr谩s de escena:

    Ahora, sirvamos el video usando una transmisi贸n:

    const http = require('http');
    const fs = require('fs');
    
    const server = http.createServer((req, res) => {
        const src = fs.createReadStream('./video.mkv');
        src.pipe(res);
    });
    
    server.listen(1337);
    

    En este c贸digo, hemos creado una secuencia legible al archivo y la canalizamos directamente a la respuesta HTTP, por lo que, en lugar de cargarla en la memoria, la entrada del disco HDD se escribe directamente en la red sin consumir memoria.

    Aqu铆 est谩 la captura de pantalla del uso de memoria mientras se env铆a el archivo usando una secuencia:

    Como puede ver, el uso de memoria es demasiado bajo en comparaci贸n con el primer m茅todo.

    Eventos 煤tiles en una secuencia

    Desde el Stream la clase hereda el EventEmitter clase, cada transmisi贸n tendr谩 su propio tipo de eventos a los que puede suscribirse mediante el EventEmitteres on() m茅todo. Este evento depender谩 del tipo de transmisi贸n.

    Eventos en transmisiones legibles

    • data: Se emite cuando se lee un fragmento de datos de la transmisi贸n. De forma predeterminada, el fragmento ser谩 un Buffer objeto. Si quieres cambiarlo puedes usar el .setEncoding() m茅todo.
    • error: Emitido cuando ocurre un error durante la lectura. Esto puede suceder si la secuencia de escritura no puede generar datos debido a alguna falla interna o cuando se inserta un fragmento no v谩lido en la secuencia.
    • end: Emitido cuando no hay m谩s datos en la transmisi贸n.
    • close: Se emite cuando se cierra el recurso de transmisi贸n e indica que no se emitir谩n m谩s eventos en el futuro.
    • readable: Emitido cuando los datos est谩n disponibles en la secuencia legible para leer.

    Eventos en transmisiones grabables

    • close: Se emite cuando se cierra el recurso de transmisi贸n e indica que no se emitir谩n m谩s eventos en el futuro.
    • error: Emitido cuando ocurre un error durante la lectura. Esto puede suceder si la secuencia de escritura no puede generar datos debido a alguna falla interna o cuando se env铆an datos de fragmentos no v谩lidos a la secuencia.
    • finish: Emitido cuando todos los datos se han vaciado de la secuencia de escritura.
    • pipe: Emitido cuando la secuencia de escritura se canaliza a una secuencia legible.
    • unpipe: Emitido cuando la secuencia de escritura no se canaliza de una secuencia legible.

    Conclusi贸n

    En t茅rminos simples, los flujos se utilizan para leer desde la entrada o escribir en la salida de forma secuencial. La mayor铆a de las veces, las transmisiones se utilizan para leer o escribir desde una fuente continua o comparativamente grande.

    El m贸dulo Stream es un m贸dulo nativo que se env铆a de forma predeterminada en Node.js. los Stream es una instancia del EventEmitter class, que maneja eventos de forma asincr贸nica en Node.js. Debido a su superclase, las transmisiones se basan inherentemente en eventos.

    Las corrientes de transformaci贸n no se trataron en este art铆culo, ya que justifican su propio art铆culo.

    El c贸digo fuente de este proyecto est谩 disponible en GitHub como siempre. Use esto para comparar su c贸digo si se atasc贸 en el tutorial.

    Si desea obtener m谩s informaci贸n sobre transmisiones o conocimientos avanzados, se recomienda seguir las documentaci贸n oficial para Streams.

    Etiquetas:

    Deja una respuesta

    Tu direcci贸n de correo electr贸nico no ser谩 publicada. Los campos obligatorios est谩n marcados con *