Ir para o conteúdo
Ferramenta para Plataforma1793 palavras

Redis para Azure: Guia Completo de Deploy

Introdução

Redis é uma daquelas ferramentas que, depois que você aprende a usar direito, passa a enxergar em praticamente todo sistema distribuído. Cache de sessão, fila de mensagens, rate limiting, ranking em tempo real: a lista de casos de uso é longa. Agora, quando você combina Redis com a infraestrutura da Azure, ganha algo que vai além de subir uma instância em uma VM qualquer. Você ganha um serviço gerenciado com replicação automática, failover transparente, integração nativa com Virtual Networks e monitoramento via Azure Monitor.

Neste guia, vou cobrir desde a criação do recurso até padrões de código em TypeScript que você pode levar direto para produção. O foco aqui é infraestrutura real: comandos CLI que funcionam, configuração que faz sentido e decisões de arquitetura que vão te poupar dor de cabeça às 3 da manhã.

Por Que Usar Redis na Azure

Vantagens do Azure Cache for Redis

O Azure Cache for Redis não é apenas um Redis hospedado. É um serviço PaaS que abstrai toda a complexidade operacional de manter um cluster Redis em produção. Você não precisa se preocupar com patching do sistema operacional, configuração de persistência em disco, ou orquestração de failover entre réplicas.

Existem três tiers disponíveis, cada um para um cenário diferente:

  • Basic: instância única sem SLA. Serve para desenvolvimento e testes.
  • Standard: duas réplicas com failover automático e SLA de 99,9%.
  • Premium: suporte a clustering, persistência em disco (RDB), Virtual Network e georéplica.
  • Enterprise: baseado no Redis Enterprise, com módulos como RediSearch, RedisJSON e RedisTimeSeries.

Casos de Uso Reais Onde Essa Combinação Brilha

Trabalho com Azure Cache for Redis há anos e os cenários onde ele se prova indispensável são consistentes. Em aplicações de e-commerce, usamos Redis para cache de catálogo de produtos e gerenciamento de sessões de carrinho de compras. Em plataformas SaaS multi-tenant, ele atua como camada de rate limiting por tenant (tema que já exploramos no post sobre Rate Limiting Inteligente com Redis e Node.js). Em sistemas de mensageria, Redis Streams substituem soluções mais pesadas quando a latência precisa ser mínima. E em dashboards de analytics, sorted sets permitem rankings e leaderboards em tempo real com complexidade O(log N).

Setup do Azure Cache for Redis com CLI

Criando o Recurso via Azure CLI

Antes de qualquer código, vamos subir a infraestrutura. Prefiro sempre usar a CLI em vez do portal: é reproduzível, versionável e pode entrar em um pipeline de CI/CD.

TYPESCRIPT
// Esses são os comandos Azure CLI que você executaria no terminal.
// Estou documentando aqui para referência.
 
// 1. Criar o resource group
// az group create --name rg-redis-producao --location brazilsouth
 
// 2. Criar a instância Redis (Standard C1 com 1GB)
// az redis create \
//   --name redis-vivocodigo-prod \
//   --resource-group rg-redis-producao \
//   --location brazilsouth \
//   --sku Standard \
//   --vm-size c1 \
//   --enable-non-ssl-port false \
//   --minimum-tls-version 1.2
 
// 3. Obter a connection string
// az redis list-keys \
//   --name redis-vivocodigo-prod \
//   --resource-group rg-redis-producao

Alguns pontos importantes sobre essa configuração. Desabilitar a porta não-SSL é obrigatório em produção. Forçar TLS 1.2 como mínimo elimina vulnerabilidades conhecidas em versões anteriores. E escolher a região brazilsouth reduz a latência para usuários no Brasil.

Configuração de Rede com Private Endpoint

Em ambientes corporativos, expor o Redis na internet pública é inaceitável. O caminho correto é usar Private Endpoints:

Text
az network private-endpoint create \
  --name pe-redis-prod \
  --resource-group rg-redis-producao \
  --vnet-name vnet-producao \
  --subnet subnet-dados \
  --private-connection-resource-id $(az redis show \
    --name redis-vivocodigo-prod \
    --resource-group rg-redis-producao \
    --query id -o tsv) \
  --group-id redisCache \
  --connection-name redis-private-connection

Com isso, o tráfego entre sua aplicação e o Redis nunca sai da rede privada da Azure.

Exemplo Prático: Cache de API com TypeScript

Conectando ao Azure Cache for Redis com ioredis

Vamos ao código. O primeiro exemplo implementa um padrão de cache-aside para respostas de API. Essa é provavelmente a integração mais comum que você vai precisar:

TYPESCRIPT
import Redis from "ioredis";
 
interface CacheConfig {
  host: string;
  port: number;
  password: string;
  tls: boolean;
  keyPrefix: string;
  defaultTTL: number;
}
 
interface CachedResponse<T> {
  data: T;
  cachedAt: string;
  source: "cache" | "origin";
}
 
class AzureRedisCache {
  private client: Redis;
  private defaultTTL: number;
 
  constructor(config: CacheConfig) {
    this.defaultTTL = config.defaultTTL;
 
    this.client = new Redis({
      host: config.host,
      port: config.port,
      password: config.password,
      tls: config.tls ? { rejectUnauthorized: true } : undefined,
      keyPrefix: config.keyPrefix,
      retryStrategy(times: number): number | null {
        if (times > 3) {
          console.error(
            `Redis: falha após ${times} tentativas. Desistindo.`
          );
          return null;
        }
        const delay = Math.min(times * 200, 2000);
        console.warn(`Redis: reconectando em ${delay}ms (tentativa ${times})`);
        return delay;
      },
      maxRetriesPerRequest: 3,
      enableReadyCheck: true,
      lazyConnect: true,
    });
 
    this.client.on("error", (err) => {
      console.error("Redis connection error:", err.message);
    });
 
    this.client.on("ready", () => {
      console.log("Redis: conexão estabelecida com sucesso");
    });
  }
 
  async connect(): Promise<void> {
    await this.client.connect();
  }
 
  async getOrFetch<T>(
    key: string,
    fetcher: () => Promise<T>,
    ttlSeconds?: number
  ): Promise<CachedResponse<T>> {
    const ttl = ttlSeconds ?? this.defaultTTL;
 
    try {
      const cached = await this.client.get(key);
 
      if (cached !== null) {
        const parsed = JSON.parse(cached) as { data: T; cachedAt: string };
        return { ...parsed, source: "cache" };
      }
    } catch (err) {
      console.warn(`Redis GET falhou para key=${key}:`, err);
      // Em caso de falha no cache, seguimos para o fetcher.
      // O sistema continua funcionando, apenas sem cache.
    }
 
    const data = await fetcher();
    const cachedAt = new Date().toISOString();
 
    try {
      const payload = JSON.stringify({ data, cachedAt });
      await this.client.set(key, payload, "EX", ttl);
    } catch (err) {
      console.warn(`Redis SET falhou para key=${key}:`, err);
    }
 
    return { data, cachedAt, source: "origin" };
  }
 
  async invalidate(pattern: string): Promise<number> {
    const keys = await this.client.keys(pattern);
    if (keys.length === 0) return 0;
 
    // Remove o prefixo porque keys() retorna com prefixo,
    // mas del() adiciona o prefixo novamente
    const pipeline = this.client.pipeline();
    for (const key of keys) {
      pipeline.del(key.replace(this.client.options.keyPrefix ?? "", ""));
    }
    await pipeline.exec();
 
    return keys.length;
  }
 
  async disconnect(): Promise<void> {
    await this.client.quit();
  }
}
 
// Uso em uma aplicação real
const cache = new AzureRedisCache({
  host: "redis-vivocodigo-prod.redis.cache.windows.net",
  port: 6380,
  password: process.env.REDIS_PRIMARY_KEY!,
  tls: true,
  keyPrefix: "api:v1:",
  defaultTTL: 300,
});
 
async function handleProductRequest(productId: string) {
  await cache.connect();
 
  const result = await cache.getOrFetch(
    `product:${productId}`,
    async () => {
      // Simulando chamada ao banco de dados
      const response = await fetch(
        `https://api.internal/products/${productId}`
      );
      return response.json();
    },
    600 // Cache de 10 minutos para produtos
  );
 
  console.log(`Produto retornado via ${result.source}`);
  return result;
}

O detalhe mais importante desse código é a estratégia de fallback. Quando o Redis falha (e em produção, ele vai falhar eventualmente), a aplicação continua funcionando. O cache nunca deve ser um ponto único de falha.

Exemplo Prático: Sessões Distribuídas com Express

Gerenciamento de Sessão em Cluster

O segundo exemplo resolve um problema clássico de aplicações distribuídas: sessões compartilhadas entre múltiplas instâncias.

TYPESCRIPT
import express from "express";
import session from "express-session";
import RedisStore from "connect-redis";
import Redis from "ioredis";
 
function createSessionMiddleware(): express.RequestHandler {
  const redisClient = new Redis({
    host: "redis-vivocodigo-prod.redis.cache.windows.net",
    port: 6380,
    password: process.env.REDIS_PRIMARY_KEY!,
    tls: { rejectUnauthorized: true },
    retryStrategy(times: number): number | null {
      if (times > 5) return null;
      return Math.min(times * 300, 3000);
    },
    db: 1, // Usar database separada para sessões
  });
 
  redisClient.on("error", (err) => {
    console.error("Redis Session Store error:", err.message);
  });
 
  const store = new RedisStore({
    client: redisClient,
    prefix: "sess:",
    ttl: 86400, // 24 horas em segundos
    disableTouch: false, // Renovar TTL a cada acesso
  });
 
  return session({
    store,
    secret: process.env.SESSION_SECRET!,
    resave: false,
    saveUninitialized: false,
    name: "sid",
    cookie: {
      secure: true,
      httpOnly: true,
      maxAge: 86400000, // 24 horas em milissegundos
      sameSite: "strict",
    },
  });
}
 
const app = express();
app.use(createSessionMiddleware());
 
app.post("/api/login", async (req, res) => {
  // Após autenticação bem-sucedida
  req.session.userId = "user-123";
  req.session.tenant = "empresa-abc";
  req.session.roles = ["admin", "editor"];
 
  res.json({ message: "Login realizado com sucesso" });
});
 
app.get("/api/profile", (req, res) => {
  if (!req.session.userId) {
    return res.status(401).json({ error: "Não autenticado" });
  }
 
  res.json({
    userId: req.session.userId,
    tenant: req.session.tenant,
    roles: req.session.roles,
  });
});
 
app.listen(3000, () => {
  console.log("Servidor rodando na porta 3000");
});

Usar db: 1 para sessões é uma prática que recomendo fortemente. Separar sessões do cache de API permite que você faça FLUSHDB no database de cache sem destruir as sessões dos seus usuários.

Boas Práticas para Redis na Azure em Produção

Configuração e Sizing

Escolher o tamanho correto da instância é mais arte do que ciência, mas existem regras confiáveis. Comece monitorando a métrica usedmemory no Azure Monitor. Se ela ultrapassar 80% de forma consistente, é hora de escalar. Para o tier Premium com clustering, distribua os dados em pelo menos 3 shards para balancear a carga de forma adequada.

Segurança

Nunca, em hipótese alguma, exponha seu Redis na internet pública. Use Private Endpoints conforme demonstrado acima. Rotacione as chaves de acesso periodicamente com o comando:

Text
az redis regenerate-keys \
  --name redis-vivocodigo-prod \
  --resource-group rg-redis-producao \
  --key-type Primary

Considere usar Managed Identity em vez de connection strings quando possível. Isso elimina o risco de credenciais vazadas em repositórios de código.

Monitoramento e Alertas

Configure alertas no Azure Monitor para as métricas que realmente importam: Server Load acima de 70%, Cache Misses em tendência de alta, Connected Clients próximo do limite e Used Memory RSS acima de 80%. Um dashboard com essas quatro métricas já cobre 90% dos problemas que você vai encontrar.

Padrões de Resiliência

Implemente circuit breaker na camada de cache. Se o Redis ficar indisponível, sua aplicação deve degradar graciosamente e buscar os dados direto da origem. O exemplo de código do getOrFetch acima já implementa esse padrão com os blocos try/catch.

Use TTLs sempre. Dados sem expiração são o caminho mais rápido para um OOM (Out of Memory) em produção. Mesmo para dados que "nunca mudam", defina um TTL generoso de 24 horas como salvaguarda.

Serialização e Chaves

Padronize o formato das suas chaves usando separadores com dois pontos, como api:v1:product:123. Isso facilita a inspeção manual, permite invalidação por padrão com KEYS ou SCAN, e mantém o Redis organizado quando você tem centenas de milhares de chaves.

Para serialização, JSON funciona bem na maioria dos casos. Se a performance de serialização se tornar um gargalo (algo raro, mas possível com payloads grandes), considere MessagePack como alternativa.

Conclusão

Redis na Azure é uma combinação que funciona muito bem quando configurada com cuidado. O Azure Cache for Redis elimina a carga operacional de manter a infraestrutura, enquanto você se concentra em escrever código que resolve problemas de negócio.

Os pontos que mais importam para levar daqui: use sempre TLS e Private Endpoints, implemente fallbacks para quando o cache falhar, separe databases por domínio (cache, sessões, filas) e monitore as métricas certas. Com esses fundamentos no lugar, você tem uma camada de cache que aguenta produção de verdade.

Guias Relacionados

Explore mais guias técnicos

Mais de 100 guias práticos de integração entre frameworks, ferramentas e plataformas.

Ver todos os guias