Rate Limiting em Java: da teoria à implementação prática
Rate Limiting (limitação de velocidade de requisições) — um dos mecanismos-chave de proteção e gerenciamento de carga em sistemas distribuídos modernos. Em Java, essa tarefa é resolvida tanto no nível de recursos integrados quanto com a ajuda de bibliotecas especializadas. Neste artigo, vamos analisar os principais algoritmos, bibliotecas populares e fornecer exemplos práticos de código.
Por que o Rate Limiting é necessário?
O Rate Limiting permite:
- Proteger APIs contra abusos (DDoS, tentativas de senha)
- Distribuir uniformemente a carga no backend
- Cumprir acordos de nível de serviço (SLA)
- Prevenir a ultrapassagem de cotas de serviços externos
Principais algoritmos de Rate Limiting
Antes de escolher uma biblioteca, é importante entender quais algoritmos estão por trás:
1. Token Bucket (Bucket4j, Guava)
Algoritmo clássico. Tokens são adicionados a um "balde" a uma taxa definida. Cada requisição consome um ou mais tokens. Se não houver tokens, a requisição é rejeitada.
// Exemplo em pseudocódigoclass TokenBucket { private int tokens; private long lastRefillTime; private final int capacity; private final double refillRate; // tokens por segundo
synchronized boolean tryConsume() { refill(); if (tokens > 0) { tokens--; return true; } return false; }}2. Sliding Window Log
Armazena os timestamps das últimas requisições. A cada nova requisição, remove timestamps mais antigos que a janela e verifica se o limite foi excedido. Preciso, mas requer memória para armazenar os timestamps.
3. Fixed Window Counter
Contador simples para um intervalo fixo (ex.: 100 requisições por minuto). É redefinido a cada minuto. Vulnerável a picos de carga nos limites da janela.
Bibliotecas Java populares para Rate Limiting
1. Bucket4j
Bucket4j — uma biblioteca madura que implementa completamente o algoritmo Token Bucket. Suporta cenários distribuídos via Redis, Hazelcast, JCache. Possui API Java 8+.
Instalação (Maven):
<dependency> <groupId>com.github.vladimir-bukhtoyarov</groupId> <artifactId>bucket4j-core</artifactId> <version>8.7.0</version></dependency>Exemplo de uso:
import io.github.bucket4j.*;import java.time.Duration;
public class Bucket4jExample { public static void main(String[] args) { // Cria um balde: 10 tokens, recarga de 1 token por segundo Bandwidth limit = Bandwidth.simple(10, Duration.ofSeconds(1)); Bucket bucket = Bucket4j.builder().addLimit(limit).build();
for (int i = 0; i < 15; i++) { if (bucket.tryConsume(1)) { System.out.println("Requisição #" + i + " executada"); } else { System.out.println("Requisição #" + i + " rejeitada (limite excedido)"); } } }}2. Resilience4j RateLimiter
Parte do popular framework para tolerância a falhas. Funciona com base em SemaphoreBasedRateLimiter ou AtomicRateLimiter. Integra-se facilmente com Spring Boot.
Exemplo de configuração:
import io.github.resilience4j.ratelimiter.*;import java.time.Duration;
public class Resilience4jExample { public static void main(String[] args) { RateLimiterConfig config = RateLimiterConfig.custom() .limitForPeriod(5) // 5 requisições .limitRefreshPeriod(Duration.ofSeconds(1)) // a cada segundo .timeoutDuration(Duration.ofMillis(100)) .build();
RateLimiter rateLimiter = RateLimiter.of("myService", config);
for (int i = 0; i < 8; i++) { Runnable task = R