Em sistemas distribuídos, um rate limiter é utilizado para restringir o tráfego de uma aplicação cliente para os servidores. Toda vez que um limite de requisições é atingido, o excedente é contido (em uma fila, por exemplo) para processamento posterior ou é descartado. São essenciais, mas precisam ser implementados adequadamente.
A adoção de rate limiters previne problemas de negação de serviço (DoS), em função de uso abusivo, intencional ou não, de um recurso. Por isso, eles são comuns nas arquiteturas de sistemas projetados para a escalabilidade. O Twitter, por exemplo, restringe o número de tuítes, por usuário, a 2400 por dia. No Instagram, é possível realizar no máximo 200 chamadas às APIs por hora com um mesmo token.
Além de evitar a negação de serviços, rate limiters ajudam a controlar custos, sobretudo em ambientes onde os gastos estejam associados a pagamentos para terceiros conforme ordem de uso.
Rate limiters podem ser implementados em client-side, server-side ou em um middleware. Implementações em client-side tem como mérito principal a redução da pressão sobre a rede. Já implementações em server-side ou middleware são mais “confiáveis”, por estarem em ambientes gerenciados, e robustas, visto que permitem regras mais complexas.
Como recomendação geral, os limites devem ser definidos levando-se em conta:
- uso racional máximo do sistema, como exemplificado pelos cases que indicamos no Twitter e no Instagram, impedindo anomalias causadas por erros de implementação ou comportamento malicioso.
- capacidade de recursos “gargalo”, ou seja, os mais escassos e que mais fragilizam o sistema, garantindo continuidade mesmo em cenários de estresse.
Por economia, recursos de infraestrutura deveriam ser provisionados de acordo com o uso racional do sistema (determinado a partir da análise sob a perspectiva do negócio). Estipular um “uso racional máximo” é um exagero essencial para mediar relação com clientes com demandas fora da curva, entretanto, esta não pode ser a base para o provisionamento da infraestrutura. Logo, estabelecer limites, sobretudo para os gargalos, é essencial para a garantia da estabilidade.
A adoção de rate limiters, mais que um desafio técnico (cada vez menor, devido a popularização de gateways, service meshes e WAFs, Azure API Management, etc.), é impactante para a experiência dos usuários. Aplicações que não são projetadas para “atingir o limite”, não comunicando usuários apropriadamente, nem considerando impacto na experiência, representam empecilho para o crescimento e amadorismo de quem as projeta.
Rate limiters devem ser projetados junto com as aplicações, demonstrando que o time entende os cenários de uso. Jamais deveriam ser implementados de maneira improvisada como “recurso de desesperados” para “contornar” falhas de projeto.
Excelente artigo!