Aula 39 - Tutorial Golang - Rate-Limiting
Rate-limiting
O que é rate-limiting?
Rate-limiting é uma técnica que limita a taxa de solicitações que um sistema pode receber.
É uma ferramenta importante para proteger sistemas de sobrecarga e garantir a qualidade de serviço (
QoS).
Por que usar rate-limiting?
Existem várias razões para usar
rate-limiting, incluindo:
- Proteção contra sobrecarga: Rate-limiting pode ajudar a proteger sistemas de sobrecarga, evitando que sejam inundados por solicitações. Isso pode ajudar a evitar falhas do sistema e garantir que os usuários tenham uma experiência consistente.
- Garantia de QoS: Rate-limiting pode ajudar a garantir a qualidade de serviço (QoS) para todos os usuários. Isso pode ser feito, por exemplo, garantindo que todos os usuários recebam uma resposta dentro de um tempo limite especificado.
- Prevenção de abuso: Rate-limiting pode ajudar a prevenir o abuso de sistemas, como ataques de negação de serviço (DoS).
Implementando rate-limiting em Golang
Go fornece suporte para
rate limiting usando funcionalidades do
pacote time, que faz parte de sua biblioteca padrão.
Neste exemplo específico, o
rate limiting é implementado utilizando as ferramentas de temporização fornecidas pelo pacote
time, como
tickers e
canais.
Em
Go, um "
ticker" é uma ferramenta do pacote
time utilizada para executar ações em intervalos de tempo regulares, é como um cronômetro que emite eventos periodicamente e você pode usar para controlar a frequência de determinadas operações.
OBS: Quem quiser relembrar Tickers é só clicar.
Exemplo de rate-limiting
O seguinte código mostra um exemplo de
rate-limiting:
package main
import (
"fmt"
"time"
)
func main() {
// Primeiro, vamos olhar para o rate limiting básico. Suponhamos
// que queremos limitar o processamento de requisições recebidas.
// Vamos atender essas requisições a partir de um canal
// com o mesmo nome.
requests := make(chan int, 5)
for i := 1; i <= 5; i++ {
requests <- i
}
// Fechando o canal para indicar que não haverá mais
// envio de dados, evitando deadlock
close(requests)
// Este canal `limiter` receberá um valor a cada 200 milissegundos.
// Este será o regulador no nosso esquema de rate limiting.
limiter := time.Tick(200 * time.Millisecond)
// Ao bloquear na recepção do canal `limiter` antes de atender
// cada requisição, limitamos a taxa para 1 requisição a cada 200 milissegundos.
for req := range requests {
<-limiter
fmt.Println("request", req, time.Now())
}
// Podemos querer permitir picos curtos de requisições no
// nosso esquema de rate limiting, preservando o
// limite geral de taxa. Isso pode ser alcançado por
// meio do buffer do nosso canal de limitação. Este canal `burstyLimiter`
// permitirá picos de até 3 eventos.
burstyLimiter := make(chan time.Time, 3)
// Preenchemos o canal para representar a capacidade de pico permitida.
for i := 0; i < 3; i++ {
burstyLimiter <- time.Now()
}
// A cada 200 milissegundos, tentaremos adicionar um novo
// valor ao `burstyLimiter`, até o seu limite de 3.
go func() {
for t := range time.Tick(200 * time.Millisecond) {
burstyLimiter <- t
}
}()
// Agora simulamos mais 5 requisições recebidas. As primeiras
// 3 dessas requisições se beneficiarão da capacidade de pico
// do `burstyLimiter`.
burstyRequests := make(chan int, 5)
for i := 1; i <= 5; i++ {
burstyRequests <- i
}
close(burstyRequests)
for req := range burstyRequests {
<-burstyLimiter
fmt.Println("request", req, time.Now())
}
}
Fechando o Canal requests (close(requests)) e close(burstyRequests)
Fechar um canal é uma prática importante em
Go por várias razões, entre elas, evitar
Deadlocks, além de sinalizar que não haverá mais envio de dados para esse canal.
O que é Deadlock?
Um deadlock em programação ocorre quando dois ou mais processos (
goroutines, no caso de
Go) estão esperando uns pelos outros para liberar recursos ou concluir operações, mas, nenhum deles pode prosseguir.
Isso leva a um impasse, onde todos os processos envolvidos ficam bloqueados indefinidamente, uns esperando pelos outros.
Explicação do Código
Rate Limiting
O código cria um canal de
requisições(requests) e o preenche com
5 números, representando as requisições.
Um limitador(
limiter) é configurado para emitir um sinal a cada
200 milissegundos, controlando a taxa de processamento das requisições.
Rate Limiting com Capacidade de Pico
Um canal
burstyLimiter com
buffer para
3 eventos é criado, permitindo um processamento inicial mais rápido (
pico) de até
3 requisições.
Uma
goroutine adiciona novos valores ao
burstyLimiter a cada
200 milissegundos, respeitando o
limite de
buffer.
As primeiras 3 requisições são processadas imediatamente, aproveitando a capacidade de pico, enquanto as seguintes aguardam conforme a taxa regular estabelecida.
Explicação Metafórica
- Loja (Capacidade do Serviço): A loja representa o serviço web. A capacidade da loja de atender clientes de forma eficiente é análoga à capacidade do serviço de processar requisições. Quando a loja está operando em sua capacidade máxima, isso significa que está atendendo o maior número possível de clientes ao mesmo tempo.
- Clientes (Requisições ao Serviço): Cada cliente entrando na loja representa uma requisição ao serviço. Em condições normais, a loja pode lidar com um fluxo constante de clientes entrando e sendo atendidos.
- Atendentes de Caixa e o Processo de Atendimento:
- Atendentes Regulares (
limiter): Imagine que a loja tem atendentes de caixa que atendem um cliente a cada 200 milissegundos. Isso garante que os clientes sejam atendidos de forma constante e ordenada, evitando filas longas e garantindo que a loja não fique sobrecarregada.
- Atendentes Extras para Picos (
burstyLimiter): Agora, imagine que a loja pode chamar atendentes extras para lidar com picos de clientes. Inicialmente, três atendentes extras estão disponíveis para atender rapidamente três clientes. Após esses atendimentos, a loja volta a depender dos atendentes regulares que atendem a uma taxa constante.
- Gerenciamento de Fluxo de Clientes:
- Com o
limiter, a loja nunca fica sobrecarregada, pois os atendentes regulares mantêm o fluxo de atendimento estável.
- Com o
burstyLimiter, a loja inicialmente lida com um pico de clientes, três atendimentos rápidos e depois, volta a um ritmo regular de atendimento.
Esta metáfora ilustra como um serviço web gerencia o fluxo de requisições para evitar sobrecarga, mantendo um equilíbrio entre atender a picos de demanda e garantir um serviço contínuo e estável.
É uma maneira eficiente de assegurar que todos os "clientes" (
requisições) sejam atendidos de forma justa e sem causar interrupções no "serviço da loja" (
serviço web).
Em Termos de Requisições:
O código utiliza canais em Go para implementar
rate limiting.
Sem Capacidade de Pico (Burst):
- Limitador (
limiter): Este canal é usado para limitar o processamento de requisições a uma taxa constante. No código, o limiter é um ticker que emite sinais a cada 200 milissegundos.
- Processamento de Requisições: Cada requisição é processada quando um sinal é recebido do
limiter. Isso efetivamente espaça as requisições para que ocorram a uma taxa de 1 a cada 200 milissegundos.
Com Capacidade de Pico:
- Limitador com Pico (
burstyLimiter): Este canal possui um buffer inicial que permite o processamento rápido de um número limitado de requisições (até 3 neste caso).
- Recarga do
burstyLimiter: Uma goroutine adiciona novos sinais ao burstyLimiter a cada 200 milissegundos, mas somente até o limite do seu buffer.
- Processamento de Requisições com Pico: As primeiras requisições são processadas imediatamente, até esgotar o buffer do
burstyLimiter. Após isso, as requisições subsequentes são processadas à medida que novos sinais são adicionados ao burstyLimiter.
Resumo
O código usa
tickers e
canais com
buffer para controlar a frequência de processamento de requisições.
As requisições são processadas baseadas na disponibilidade de sinais nos canais
limiter e
burstyLimiter, criando um sistema de
rate limiting eficaz que pode lidar tanto com uma taxa constante de requisições quanto com
picos de atividade.
Conclusão
Rate-limiting é uma ferramenta importante que pode ajudar a
proteger sistemas de
sobrecarga e garantir a
qualidade de serviço.
Eu fico por aqui.
Até a próxima. ;)
Meus links de afiliados:
Obrigado e bons estudos. ;)