Rate Limiting Distribuído com Token Bucket em Arquiteturas Serverless
Aprenda a controlar o fluxo de requisições em aplicações sem servidores fixos utilizando o algoritmo Token Bucket e bases de dados distribuídas para evitar abusos.
Resumo
- Arquiteturas serverless escalam infinitamente, mas deixam APIs expostas a picos de tráfego descontrolados e ataques de negação de serviço.
- O algoritmo Token Bucket funciona como um balde que armazena fichas liberadas a uma taxa constante para autorizar o acesso de novos clientes.
- Sistemas sem estado exigem o uso de armazenamentos centrais de alta velocidade, como o Redis, para sincronizar contadores entre múltiplas instâncias.
- Garantir a consistência atômica das operações de leitura e escrita evita que acessos simultâneos burlem os limites estabelecidos pela regra de negócio.
- O trade-off principal envolve aceitar latências milimétricas adicionadas pela rede em troca da proteção robusta dos serviços de back-end.
O Desafio de Controlar Acessos em Nuvem Elástica
Quando construímos aplicações modernas na nuvem utilizando funções que executam sob demanda, eliminamos a necessidade de gerenciar servidores físicos ou máquinas virtuais dedicadas. Essa flexibilidade permite que o sistema cresça instantaneamente para atender a milhões de usuários e encolha para zero quando não há tráfego. Na prática, isso significa que pagamos apenas pelos milissegundos exatos em que o código está rodando. No entanto, essa mesma elasticidade abre brechas perigosas para abusos, como robôs automatizados raspando dados ou ataques maliciosos de negação de serviço que podem inflar custos financeiros drásticos de forma silenciosa.
Proteger essas portas de entrada exige o uso de mecanismos conhecidos como controle de taxa, ou rate limiting, que funcionam como os seguranças de uma casa noturna limitando a entrada de pessoas por minuto. Em ambientes tradicionais com servidores fixos, manter esse controle é relativamente simples porque a contagem de acessos pode residir na memória local da máquina. Em arquiteturas sem servidores fixos, as funções de código nascem, processam uma requisição e morrem em frações de segundo, espalhando instâncias por dezenas de servidores ao redor do mundo. Sem um ponto central de coordenação, cada função decidiria por conta própria quem entra, quebrando completamente a regra de limite global.
Como Funciona o Algoritmo Token Bucket na Prática
Para resolver o dilema do controle de fluxo sem travar o sistema, engenheiros utilizam algoritmos matemáticos inteligentes, sendo o Token Bucket um dos mais populares e eficientes da indústria. Imagine um balde físico na recepção de um evento que recebe fichas plásticas a uma taxa constante e previsível, digamos, dez fichas por segundo. Quando um participante chega, ele precisa retirar uma ficha do balde para conseguir entrar. Se o balde estiver cheio, fichas extras simplesmente transbordam e são perdidas, estabelecendo um teto máximo de capacidade acumulada.
Aplicando essa analogia ao software, cada usuário ou endereço IP possui um balde virtual armazenado em um banco de dados de alta performance. Cada requisição HTTP que chega consome um token desse balde digital. Se houver fichas disponíveis, a requisição é processada normalmente e a ficha é subtraída. Caso o balde esteja vazio, o sistema rejeita imediatamente o acesso retornando um código de erro HTTP padrão que indica excesso de tentativas. O grande trunfo desse modelo é permitir rajadas legítimas de tráfego, já que um usuário que ficou ocioso por um tempo acumula fichas e pode realizar várias ações em sequência rápida.
Superando o Desafio Distribuído com Armazenamento Central
O calcanhar de Aquiles de qualquer solução executada em nuvem elástica é a sincronização de dados entre milhares de instâncias isoladas que processam requisições simultaneamente. Se utilizarmos variáveis locais em memória dentro de cada função serverless, um usuário astuto poderia disparar cem requisições e atingir cem instâncias diferentes, burlando completamente o limite de tráfego estabelecido. Na prática, a solução exige desacoplar o estado de controle da lógica de execução, transferindo a contagem de fichas para um banco de dados distribuído externo e ultrarrápido.
A escolha tecnológica mais comum para esse papel é o Redis, um sistema de armazenamento em memória RAM conhecido por sua velocidade estonteante de leitura e escrita. Quando uma função serverless é acionada, ela realiza uma consulta rápida ao Redis para verificar o saldo atual de tokens do cliente. Para evitar que requisições concorrentes alterem o mesmo saldo incorretamente, utilizamos operações atômicas ou scripts executados diretamente no servidor de cache. Isso garante que duas requisições chegando no exato mesmo milissegundo não consigam consumir a mesma ficha residual, mantendo a integridade matemática da regra de controle.
Implementando a Lógica em Código Funcional
Abaixo apresentamos um exemplo prático utilizando JavaScript em uma função serverless que interage com o Redis para aplicar a lógica do Token Bucket. O código calcula quantos tokens devem ser repostos com base no tempo decorrido desde a última requisição do usuário antes de permitir ou negar o acesso.
const redis = require('redis');
const client = redis.createClient();
async function checkRateLimit(userId) {
const key = `rate:bucket:${userId}`;
const now = Date.now();
const capacity = 10;
const refillRate = 1; // tokens por segundo
let bucket = await client.hGetAll(key);
let tokens = bucket.tokens ? parseFloat(bucket.tokens) : capacity;
let lastUpdate = bucket.lastUpdate ? parseInt(bucket.lastUpdate) : now;
const elapsed = (now - lastUpdate) / 1000;
tokens = Math.min(capacity, tokens + elapsed * refillRate);
if (tokens < 1) {
return { allowed: false, remaining: 0 };
}
tokens -= 1;
await client.hSet(key, { tokens, lastUpdate: now });
return { allowed: true, remaining: Math.floor(tokens) };
}Esse trecho de código demonstra a simplicidade conceitual combinada com o rigor técnico necessário para ambientes de alta volumetria. Ao armazenar o estado de forma estruturada no Redis, garantimos que qualquer nova instância de função serverless acionada em seguida terá acesso imediato e preciso ao histórico recente daquele usuário específico.
Considerações Operacionais e Trade-Offs de Arquitetura
Adotar uma estratégia distribuída de controle de acesso traz vantagens inegáveis de segurança, mas também introduz custos e complexidades operacionais que precisam ser avaliados com cautela. O primeiro trade-off relevante é a adição de latência na rede. Enquanto funções serverless executam tipicamente perto do usuário em bordas geograficamente distribuídas, o banco de dados centralizado pode estar localizado em uma região específica, adicionando dezenas de milissegundos preciosos ao tempo total de resposta da API.
Outro ponto crítico de atenção diz respeito à resiliência e à tolerância a falhas. Se o banco de dados centralizado de cache sofrer uma indisponibilidade temporária, toda a cadeia de microsserviços pode ficar inacessível caso a aplicação não possua um mecanismo de fallback adequado. Em cenários de alta criticidade, os engenheiros frequentemente optam por permitir o tráfego temporariamente em caso de falha no Redis, priorizando a disponibilidade do negócio em detrimento do bloqueio rigoroso de abusos durante breves janelas de instabilidade técnica. Para complementar essa implementação em produção, vale conferir também nossas análises técnicas sobre Rate Limiting Distribuído em Serverless com Token Bucket e Redis Cluster e Isolamento de Microsserviços e Rate Limiting com Token Bucket Distribuído.
Considerações Finais
Implementar controle de taxa distribuído utilizando o algoritmo Token Bucket em ambientes sem servidores fixos exige o alinhamento cuidadoso entre escalabilidade, consistência de dados e velocidade de resposta. Embora a infraestrutura elástica traga desafios inéditos de sincronização, o uso de armazenamentos em memória de alta performance resolve o problema de forma elegante e segura. Dominar esses conceitos garante que suas aplicações permaneçam protegidas, resilientes e financeiramente previsíveis mesmo sob picos extremos de acesso global.