Cache Distribuído com Redis e Node.js: Guia Prático de Implementação

Cache Distribuído com Redis e Node.js: Guia Prático de Implementação

# Cache Distribuído com Redis e Node.js: Guia Prático de Implementação...

6 min de leitura
🔒 Faça login para curtir

Autor

Luis Henrique Cuba

Luis Henrique Cuba

Autor no blog LHCX.

Gostou do conteúdo?

🔒 Faça login para curtir

Sua curtida nos ajuda a melhorar

Cache Distribuído com Redis e Node.js: Guia Prático de Implementação

Tecnologia e Inovação

Introdução

Em aplicações web modernas, a latência de acesso a dados pode ser o principal gargalo de desempenho. Quando múltiplas instâncias de um serviço precisam compartilhar informações rapidamente, o cache distribuído surge como solução eficaz. Neste artigo, vamos explorar como implementar um cache distribuído usando Redis como camada de armazenamento em memória e Node.js como runtime de aplicação.

Entenderemos os conceitos fundamentais, configuraremos o Redis em ambiente de desenvolvimento (Docker), integraremos ao Express e aplicaremos estratégias de expiração e invalidação. Ao final, você terá um código pronto para produção que reduz drasticamente o tempo de resposta de consultas frequentes.


Desenvolvimento

1. Conceitos de Cache Distribuído

  • Cache local vs. distribuído: o cache local (em memória da própria aplicação) é rápido, mas não compartilha dados entre instâncias. O cache distribuído, como o Redis, mantém um único ponto de verdade acessível por todas as réplicas.
  • Consistência: em sistemas de alta escala, a consistência eventual costuma ser suficiente; o Redis oferece operações atômicas que ajudam a manter a coerência.
  • TTL (Time‑To‑Live): define quanto tempo um registro permanece válido no cache, evitando dados obsoletos.

2. Configurando o Redis

A forma mais simples de iniciar um servidor Redis para desenvolvimento é via Docker:

bash

docker-compose.yml

version: "3.8" services: redis: image: redis:7-alpine container_name: redis-cache ports: - "6379:6379" restart: unless-stopped command: ["redis-server", "--appendonly", "yes"]

Execute:

bash docker compose up -d

O Redis ficará disponível em localhost:6379. Você pode testar a conexão com o cliente CLI:

bash redis-cli ping

Resposta: PONG

Desenvolvimento e Código

3. Integrando Redis ao Node.js

Usaremos o pacote ioredis, que oferece suporte a cluster e reconexão automática.

bash npm install express ioredis

3.1. Criação da camada de cache

javascript // cache.js const Redis = require('ioredis'); const redis = new Redis({ host: process.env.REDIS_HOST || '127.0.0.1', port: process.env.REDIS_PORT || 6379, });

/**

  • Obtém um valor do cache ou executa a função de fallback.
  • @param {string} key Chave única para o item.
  • @param {function} fetchFn Função async que busca o dado na origem.
  • @param {number} ttl Tempo em segundos que o item ficará no cache. */ async function getOrSet(key, fetchFn, ttl = 300) { const cached = await redis.get(key); if (cached) { return JSON.parse(cached); } const fresh = await fetchFn(); await redis.set(key, JSON.stringify(fresh), 'EX', ttl); return fresh; }

module.exports = { getOrSet, redis };

3.2. Middleware de cache para rotas Express

javascript // middleware/cacheMiddleware.js const { getOrSet } = require('../cache');

function cache(ttl) { return async (req, res, next) => { const key = __express__${req.originalUrl}; try { const data = await getOrSet(key, async () => null, ttl); if (data) { return res.json(data); } // Se não houver cache, delega ao próximo handler res.locals.cacheKey = key; // salva a chave para uso posterior next(); } catch (err) { next(err); } }; }

module.exports = cache;

3.3. Exemplo de API usando o cache

javascript // index.js const express = require('express'); const axios = require('axios'); const cache = require('./middleware/cacheMiddleware'); const { redis } = require('./cache');

const app = express(); const PORT = process.env.PORT || 3000;

// Rota que consome um endpoint externo (ex.: JSONPlaceholder) app.get('/posts/:id', cache(120), async (req, res, next) => { const { id } = req.params; try { const response = await axios.get(https://jsonplaceholder.typicode.com/posts/${id}); const result = response.data; // Salva no cache antes de responder await redis.set(res.locals.cacheKey, JSON.stringify(result), 'EX', 120); res.json(result); } catch (err) { next(err); } });

app.listen(PORT, () => { console.log(🚀 API rodando na porta ${PORT}); });

Neste fluxo, a primeira requisição a /posts/1 busca o dado externo, armazena no Redis por 2 minutos e devolve ao cliente. As chamadas subsequentes dentro desse intervalo retornam imediatamente do cache.

4. Estratégias de Expiração e Invalidação

  • TTL curto para dados voláteis (ex.: sessões, contadores). Use valores de 30‑300 s.
  • TTL longo para catálogos estáticos (ex.: lista de produtos). Pode chegar a 24 h.
  • Cache‑aside (lazy loading): como no exemplo acima, o dado só entra no cache quando solicitado.
  • Write‑through: ao atualizar a fonte de dados, escreva simultaneamente no Redis para evitar stale reads.
  • Invalidar por evento: ao receber um webhook de atualização, chame redis.del(key).

javascript // Exemplo de invalidação ao atualizar um produto app.put('/products/:id', async (req, res, next) => { const { id } = req.params; const payload = req.body; // ... lógica de atualização no DB await redis.del(product:${id}); // remove versão antiga do cache res.json({ status: 'atualizado' }); });


Exemplos Práticos

1. Deploy do Redis em produção (AWS Elasticache)

Embora o Docker seja ótimo para desenvolvimento, em produção recomenda‑se um serviço gerenciado. No AWS Elasticache, basta criar um cluster Redis, habilitar Encryption in‑Transit e apontar a aplicação para o endpoint fornecido.

javascript // Configuração de produção (env) const redis = new Redis({ host: process.env.REDIS_ENDPOINT, // ex.: mycluster.xxxxxx.ng.0001.use1.cache.amazonaws.com port: 6379, tls: {}, // ativa TLS });

2. Monitoramento com redis-cli e INFO

bash

Ver estatísticas de uso

redis-cli INFO memory

Limpar todo o cache (cuidado!)

redis-cli FLUSHALL

3. Teste de carga com k6

javascript import http from 'k6/http'; import { check, sleep } from 'k6';

export const options = { stages: [{ duration: '30s', target: 100 }], // 100 VUs };

export default function () { const res = http.get('http://localhost:3000/posts/1'); check(res, { 'status 200': (r) => r.status === 200 }); sleep(1); }

A diferença de latência entre a primeira (hit‑miss) e as seguintes (hit) costuma ser de ~200 ms para a origem versus <5 ms para o Redis.


Conclusão

Implementar um cache distribuído com Redis e Node.js traz ganhos de desempenho palpáveis, especialmente em APIs que consultam fontes externas ou bancos de dados relacionalmente pesados. Seguindo as boas práticas de TTL, invalidação baseada em eventos e monitoramento constante, é possível manter a consistência dos dados sem sacrificar a velocidade.

Próximos passos:

  1. Avaliar a necessidade de um cluster Redis para alta disponibilidade.
  2. Integrar métricas ao Prometheus/Grafana.
  3. Explorar recursos avançados como Redis Streams ou Lua scripting para lógica de cache mais sofisticada.

Com a base apresentada, sua aplicação está pronta para escalar de forma eficiente e resiliente.

Tecnologia e Negócios

Tags

Carregando comentários...