DEV Community

Vitor Rios
Vitor Rios

Posted on

Benefícios do Uso de Streams em Node.js

Introdução

No processamento de grandes volumes de dados, o uso de streams em Node.js pode trazer enormes vantagens em termos de performance e eficiência. Streams permitem o processamento de dados de forma contínua e em pedaços, evitando o carregamento completo do arquivo na memória. Este artigo explora os benefícios do uso de streams, utilizando um exemplo prático para demonstrar como transformar um arquivo de texto grande de forma eficiente.

O Que São Streams?

Streams são uma abstração em Node.js que permite o processamento de dados em pedaços (chunks) ao invés de carregar tudo na memória de uma vez. Existem quatro tipos principais de streams em Node.js:

  1. Readable: streams de onde podemos ler dados.
  2. Writable: streams para onde podemos escrever dados.
  3. Duplex: streams que são tanto readable quanto writable.
  4. Transform: streams que podem modificar ou transformar os dados enquanto passam por elas.

Benefícios dos Streams

1. Eficiência de Memória

Usando streams, os dados são processados em pedaços, o que significa que você não precisa carregar um arquivo inteiro na memória. Isso é crucial para grandes arquivos, pois evita problemas de memória e melhora a performance do sistema.

2. Processamento de Dados em Tempo Real

Streams permitem o processamento contínuo de dados. Por exemplo, você pode começar a processar os primeiros pedaços de dados enquanto ainda está recebendo os próximos, o que resulta em um tempo de processamento total menor.

3. Manutenção da Responsividade

Ao não bloquear o Event Loop de Node.js, os streams ajudam a manter a aplicação responsiva, mesmo durante operações intensivas de I/O.

Exemplo Prático

Gerando um Arquivo de Teste

Antes de iniciar, vamos criar um arquivo de texto grande para teste. Você pode usar o seguinte script em Python para gerar um arquivo de 10GB:

# generator.py

# Define o tamanho do arquivo em bytes (10GB)
file_size = 10000 * 1024 * 1024  # 10 GB

# Linha que será escrita repetidas vezes no arquivo
line = "This is a line of text to be transformed. Adding more text to increase the size of each line.\n"

# Calcula o número de linhas necessárias para preencher o arquivo
num_lines = file_size // len(line)

# Cria e escreve o arquivo
file_path = "large-input.txt"
with open(file_path, "w") as file:
    for _ in range(num_lines):
        file.write(line)

print(f"File created successfully at {file_path}")
Enter fullscreen mode Exit fullscreen mode

Para rodar o script acima, salve-o como generator.py e execute-o usando o comando:

python3 generator.py
Enter fullscreen mode Exit fullscreen mode

Transformando o Arquivo Usando Streams

Aqui está o código em Node.js que transforma o conteúdo de large-input.txt para letras maiúsculas e salva o resultado em large-output.txt. Ele também exibe o progresso a cada 10% e o tempo total do processo.

// src/index.js

const fs = require('fs');
const { Transform } = require('stream');
const { performance } = require('perf_hooks');

// Caminho para o arquivo de entrada e saída
const inputFile = 'large-input.txt';
const outputFile = 'large-output.txt';

// Cria um Readable Stream a partir do arquivo de entrada
const readableStream = fs.createReadStream(inputFile, { encoding: 'utf8' });

// Cria um Writable Stream para o arquivo de saída
const writableStream = fs.createWriteStream(outputFile);

// Variáveis para rastreamento de progresso
let totalSize = 0;
let processedSize = 0;
let lastLoggedProgress = 0;
const startTime = performance.now();
let processedLines = 0;

fs.stat(inputFile, (err, stats) => {
  if (err) {
    console.error('Erro ao obter informações do arquivo:', err);
    return;
  }
  totalSize = stats.size;

  // Pipe o Readable Stream para o Transform Stream e depois para o Writable Stream
  readableStream
    .pipe(
      new Transform({
        transform(chunk, encoding, callback) {
          processedSize += chunk.length;
          processedLines += chunk.toString().split('\n').length - 1;

          // Converte o chunk de dados para letras maiúsculas
          const upperCaseChunk = chunk.toString().toUpperCase();

          // Chama o callback com o chunk transformado
          callback(null, upperCaseChunk);

          // Log de progresso
          const progress = (processedSize / totalSize) * 100;

          if (progress >= lastLoggedProgress + 10) {
            console.log(
              `Progresso: ${Math.floor(progress)}%, Linhas processadas: ${processedLines}`
            );
            lastLoggedProgress = Math.floor(progress);
          }
        },
      })
    )
    .pipe(writableStream)
    .on('finish', () => {
      const endTime = performance.now();
      const timeTaken = ((endTime - startTime) / 1000).toFixed(2);
      console.log('Transformação completa e arquivo salvo.');
      console.log(`Total de linhas processadas: ${processedLines}`);
      console.log(`Tempo total: ${timeTaken} segundos`);
    })
    .on('error', (err) => {
      console.error('Erro durante a transformação:', err);
    });
});
Enter fullscreen mode Exit fullscreen mode

Benefícios desta Abordagem

  1. Eficiência de Memória: O uso de streams permite processar grandes volumes de dados sem carregar todo o arquivo na memória, evitando estouro de memória e melhorando a performance.
  2. Melhor Performance: Processar dados em pedaços contínuos permite iniciar o processamento imediatamente, sem esperar o carregamento completo do arquivo.
  3. Feedback em Tempo Real: A exibição do progresso em tempo real proporciona uma visão clara do andamento do processamento, permitindo monitoramento e intervenção rápida se necessário.

Conclusão

Streams são uma ferramenta poderosa em Node.js para manipulação de grandes volumes de dados. Usando streams, você pode processar arquivos de maneira eficiente, mantendo a aplicação responsiva e evitando problemas de memória. O exemplo acima demonstra como transformar um arquivo de texto grande usando streams, exibindo o progresso e o tempo total do processo.

Para mais detalhes e acesso ao código completo, visite meu repositório no GitHub.

Top comments (0)