Marcio Cunha

Rate Limiting Distribuído com Redis Cluster e Token Bucket em Alta Concorrência

Aprenda a projetar sistemas de controle de tráfego em larga escala usando Redis Cluster e o algoritmo Token Bucket para proteger APIs contra sobrecargas.

Marcio Cunha•5 min
Também disponível em:EnglishEspañol
Resumo
  • O algoritmo Token Bucket equilibra rajadas de tráfego e consumo sustentado sem descartar requisições legítimas prematuramente.
  • A fragmentação de dados em Redis Cluster evita gargalos de memória e CPU quando o volume de requisições atinge milhões por segundo.
  • Scripts Lua atômicos eliminam condições de corrida em operações de leitura e escrita concorrentes no banco de dados em memória.
  • A latência de rede entre nós distribuídos exige estratégias de fallback tolerantes a falhas para garantir disponibilidade contínua.
  • O monitoramento contínuo de chaves quentes previne desbalanceamento de carga severo entre os shards do cluster Redis.

O Desafio de Proteger APIs em Ambientes Distribuídos

Quando múltiplos servidores processam bilhões de requisições simultâneas, proteger a infraestrutura contra abusos torna-se um requisito vital de engenharia. Na prática, isso significa impedir que scrapers maliciosos, bugs de software ou picos repentinos de acesso derrubem os serviços principais, garantindo estabilidade para usuários legítimos. O rate limiting, ou limitação de taxa, atua como um porteiro inteligente que decide quem entra e quem deve aguardar na fila.

Em arquiteturas modernas baseadas em microsserviços, essa tarefa deixa de ser simples. Como os nós de aplicação escalam horizontalmente e rodam em servidores diferentes, manter o controle global do número de requisições exige um componente centralizado e extremamente rápido. Sem uma coordenação eficiente, os sistemas falham em contabilizar o tráfego total, permitindo que clientes ultrapassem os limites estabelecidos simplesmente alternando entre diferentes instâncias da API.

Entendendo o Algoritmo Token Bucket na Prática

Existem várias formas matemáticas de controlar o fluxo de dados, mas o algoritmo Token Bucket, ou balde de tokens, destaca-se pela flexibilidade. Imagine um balde físico que recebe água a uma taxa constante, digamos, dez gotas por segundo, até atingir sua capacidade máxima. Cada requisição que chega retira uma gota desse balde para conseguir ser processada pelo sistema.

Se o usuário fizer uma rajada rápida de requisições, o balde consegue atendê-la instantaneamente, desde que haja tokens acumulados suficientes dentro dele. Quando o balde esvazia completamente, novas requisições começam a ser rejeitadas ou colocadas em espera até que o tempo passe e novos tokens sejam gerados. Na prática, esse comportamento permite picos legítimos de uso sem penalizar o usuário, ao mesmo tempo em que protege o backend contra sobrecargas contínuas.

Por que o Redis Cluster é a Escolha Ideal

Para implementar essa lógica em larga escala, precisamos de um armazenamento de dados ultrarrápido que suporte milhões de operações por segundo com latência na casa dos microssegundos. O Redis atende perfeitamente a essa necessidade por manter todos os dados na memória RAM, eliminando a lentidão típica dos discos rígidos tradicionais. Em ambientes corporativos de altíssimo tráfego, um único nó de Redis pode saturar a CPU ou atingir o limite físico de memória.

É aí que entra o Redis Cluster, uma topologia distribuída que divide os dados em múltiplos fragmentos, chamados de shards, espalhados por vários servidores. Com essa abordagem, a carga de trabalho é distribuída de forma inteligente, permitindo que o sistema escale horizontalmente conforme o tráfego da aplicação aumenta. No entanto, coordenar chaves distribuídas traz novos desafios de consistência que precisam ser tratados diretamente no nível do código.

Garantindo Atomicidade com Scripts Lua

Um dos maiores perigos em sistemas concorrentes é a chamada condição de corrida, que ocorre quando duas requisições leem e modificam o mesmo saldo de tokens exatamente no mesmo microssegundo. Sem um mecanismo de bloqueio, o sistema pode calcular errado o número de tokens restantes, permitindo acessos indevidos. Para resolver esse problema sem prejudicar a performance, utilizamos scripts escritos na linguagem Lua executados diretamente dentro do Redis.

O Redis executa scripts Lua de forma estritamente atômica, o que significa que nenhuma outra operação consegue interromper o código enquanto ele estiver rodando. Na prática, o script calcula o tempo decorrido, repõe os tokens devidos no balde, verifica se há saldo suficiente para a requisição atual e atualiza o estado, tudo em uma única transação indivisível. Abaixo está um exemplo prático de implementação desse script em um ambiente Node.js utilizando Redis:

const Redis = require('ioredis');
const redis = new Redis.Cluster([{ host: '127.0.0.1', port: 7000 }]);

const tokenBucketScript = `
  local key = KEYS[1]
  local capacity = tonumber(ARGV[1])
  local fillRate = tonumber(ARGV[2])
  local requested = tonumber(ARGV[3])
  local now = tonumber(ARGV[4])

  local bucket = redis.call('hmget', key, 'tokens', 'last_updated')
  local tokens = tonumber(bucket[1])
  local last_updated = tonumber(bucket[2])

  if not tokens then
    tokens = capacity
    last_updated = now
  else
    local elapsed = math.max(0, now - last_updated)
    tokens = math.min(capacity, tokens + (elapsed * fillRate))
    last_updated = now
  end

  if tokens < requested then
    return {0, tokens}
  else
    tokens = tokens - requested
    redis.call('hmset', key, 'tokens', tokens, 'last_updated', last_updated)
    return {1, tokens}
  end
`;

async function checkRateLimit(userId, capacity, fillRate, cost) {
  const now = Math.floor(Date.now() / 1000);
  const result = await redis.eval(tokenBucketScript, 1, `rate:{userId}`, capacity, fillRate, cost, now);
  return result[0] === 1;
}

Mitigando Falhas e Gerenciamento de Chaves Quentes

Mesmo com uma arquitetura robusta, imprevistos acontecem e nós do Redis Cluster podem falhar durante picos de acesso. Para evitar que a indisponibilidade do cache derrube a API inteira, é fundamental implementar políticas de fallback adequadas. Na prática, se o cluster Redis deixar de responder por qualquer motivo, o middleware de controle de tráfego deve permitir a passagem temporária das requisições, priorizando a disponibilidade do negócio sobre o bloqueio estrito.

Outro problema crítico é o fenômeno das chaves quentes, que acontece quando um único usuário ou recurso gera um volume massivo de requisições concentradas em um único shard do cluster. Como o Redis Cluster distribui as chaves com base em hashes, chaves com o mesmo prefixo podem cair no mesmo nó, criando um ponto único de estrangulamento. O uso de técnicas de hash tagging, que forçam chaves específicas a caírem em nós alternativos, ajuda a espalhar o esforço computacional e manter o sistema estável.

Considerações Finais

Construir um sistema de rate limiting distribuído exige equilibrar precisão matemática, performance de rede e resiliência operacional. O uso combinado do Redis Cluster com o algoritmo Token Bucket garante que aplicações de alta concorrência consigam absorver tráfego intenso sem comprometer a integridade dos servidores de backend.

Compreender os trade-offs envolvidos na atomicidade de scripts e no tratamento de falhas permite que engenheiros desenhem sistemas escaláveis e preparados para suportar o crescimento explosivo de usuários reais. A escolha consciente das ferramentas e o monitoramento contínuo dos nós formam a base indispensável para manter qualquer serviço digital seguro e disponível.