O que é throttling e como implementar em Go
Em uma das minhas sessões de mentoria, me perguntaram sobre throttling.
Throttling é uma técnica utilizada em diversas áreas da tecnologia para controlar o uso excessivo de recursos, limitando a taxa de execução de certa ação em um determinado período de tempo, podendo adicionar os eventos extras à uma fila de processamento ou descartá-los por completo.
Entendendo o throttling
Imagine um restaurante muito popular que recentemente viralizou nas redes sociais. Suponha que a cozinha do restaurante só consiga preparar até 10 pratos por hora, devido à capacidade dos cozinheiros e dos equipamentos.
Se o restaurante simplesmente tentar receber todo mundo que chega, ele terá problemas:
- Os cozinheiros serão inundados com mais pedidos do que podem lidar
- A qualidade dos pratos provavelmente cairá, pois os cozinheiros tentariam preparar os pratos com pressa
- O tempo de espera dos clientes aumentará significativamente, e muitos iriam embora insatisfeitos
- Faltarão mesas, e clientes ficarão em pé
Agora, considere que o restaurante implementou um limite ao número de clientes que atende simultaneamente.
- Cada cliente que chega ao restaurante recebe um número de acordo com a ordem de chegada
- Se já houver 10 clientes sendo atendidos, qualquer novo cliente precisa esperar sua vez na fila antes de fazer o pedido
- À medida que a cozinha termina um prato e o entrega ao cliente, o próximo da fila pode fazer seu pedido
A fila garante que todos os clientes sejam atendidos de forma justa e em ordem de chegada. Isso mantém um fluxo contínuo e controlado de pedidos sendo processados.
A cozinha do restaurante é equivalente ao servidor ou à capacidade de processamento de uma API
Os clientes chegando ao restaurante representam usuários ou serviços que fazem requisições ao servidor
A fila de espera é o mecanismo de fila (queue) usado para gerenciar requisições que excedem a capacidade do sistema
O limite de 10 pratos por hora é o throttling, limitando o número de requisições que o servidor pode processar em um dado período de tempo.
Implementando throttling em Go
Vamos implementar um exemplo simples de throttling em Go.
Neste exemplo, vamos limitar a execução de uma função a 5 vezes por segundo.
Channels
Os channels são uma das principais ferramentas para comunicação entre goroutines em Go. Eles são usados para enviar e receber dados entre goroutines.
É possível verificar se um channel é capaz de receber um evento imediatamente ou se ele está bloqueado. Esse será o princípio básico para implementar nosso throttling.
Exemplo
package main
import (
"fmt"
"time"
)
var interval = 5 * time.Second
func monitor(ch <-chan string) {
ticker := time.NewTicker(interval)
for {
select {
case <-ticker.C:
fmt.Println(<-ch)
}
}
}
func main() {
var ch = make(chan string, 1)
go monitor(ch)
for i := 0; i < 100; i++ {
select {
case ch <- fmt.Sprintf("Message %d", i):
default:
fmt.Println("Channel is full!")
}
time.Sleep(1 * time.Second)
}
}
Vamos entender o que está acontecendo no código acima:
1. Pacote principal e importação de bibliotecas
package main
import (
"fmt"
"time"
)
package main
: Define o pacote principal do programa Go, necessário para a execução.import "fmt"
eimport "time"
: Importa os pacotes para formatação de strings e manipulação de tempo, respectivamente.
2. Variáveis globais
var interval = 5 * time.Second
interval
: Essa variável global define o intervalo de tempo de 5 segundos para o throttling. Ou seja, a funçãomonitor
somente aceitará mensagens a cada 5 segundos.
3. Função monitor
func monitor(ch <-chan string) {
ticker := time.NewTicker(interval)
for {
select {
case <-ticker.C:
fmt.Println(<-ch)
}
}
}
monitor(ch <-chan string)
: Uma função que recebe um channel somente de leitura do tipostring
.ticker := time.NewTicker(interval)
: Cria um ticker que envia um evento a cada intervalo de tempo definido (5 segundos neste exemplo).for { select { ... } }
: Inicia um loop infinito que usa uma construçãoselect
para lidar com operações de recebimento no channel.case <-ticker.C: fmt.Println(<-ch)
: Quando o ticker envia um evento (a cada 5 segundos), um valor é recebido do channelch
e impresso.
4. Função main
func main() {
var ch = make(chan string, 1)
go monitor(ch)
for i := 0; i < 100; i++ {
select {
case ch <- fmt.Sprintf("Message %d", i):
default:
fmt.Println("Channel is full!")
}
time.Sleep(1 * time.Second)
}
}
var ch = make(chan string, 1)
: Cria um channel destring
com buffer de tamanho 1.go monitor(ch)
: Inicia a funçãomonitor
em uma goroutine, permitindo que ela funcione de forma assíncrona.- O laço
for i := 0; i < 100; i++ { ... }
tenta enviar 100 mensagens para o channelch
:select { case ch <- fmt.Sprintf("Message %d", i): default: fmt.Println("Channel is full!") }
: Tenta enviar a mensagem formatada para o channel. Se o channel estiver cheio (o que pode acontecer, já que o buffer é de tamanho 1 e as mensagens são processadas apenas a cada 5 segundos), odefault
é executado e a mensagem “Channel is full!” é impressa.time.Sleep(1 * time.Second)
: O loop espera 1 segundo antes de tentar enviar a próxima mensagem.
Resumo
- Este código implementa throttling ao combinar channels e tickers para processar mensagens a cada 5 segundos.
- A função
monitor
é usada para consumir as mensagens do channel de forma controlada (a cada 5 segundos). - A função
main
tenta enviar 100 mensagens para o channel com uma espera de 1 segundo entre cada tentativa. Se o channel estiver cheio, uma mensagem de aviso é impressa.
Testando o código
Para testar o código, basta salvá-lo em um arquivo Go (por exemplo, throttling.go
) e executá-lo.
Você verá a saída das mensagens sendo impressas a cada 5 segundos, devido ao throttling implementado,
go run throttling.go
Channel is full!
Channel is full!
Channel is full!
Channel is full!
Message 0
Channel is full!
Channel is full!
Channel is full!
Channel is full!
Message 5
Channel is full!
Channel is full!
Channel is full!
Channel is full!
Message 10
...
Este é um exemplo simples de como implementar throttling em Go. Você pode ajustar o intervalo de tempo, o tamanho do buffer do channel e outras configurações para atender às necessidades específicas do seu aplicativo.
Ao invés de imprimir Channel is full
, você poderia adicionar os eventos extras à uma fila de processamento ou descartá-los por completo, dependendo dos requisitos do seu aplicativo.
Espero que este exemplo tenha sido útil para entender o conceito de throttling e como implementá-lo em Go. Se tiver alguma dúvida ou sugestão, fique à vontade para compartilhar nos comentários.
Até a próxima!