Aula 39 – Tutorial Golang – Rate-Limiting

Aula 39 – Tutorial Golang – Rate-Limiting

Tutorial Golang

Tutorial Golang

Página principal do blog

Todas as aulas desse curso

Aula 38                        Aula 40

Redes Sociais:

facebook

Meus links de afiliados:

Hostinger

Digital Ocean

One.com

Melhore seu NETWORKING

https://digitalinnovation.one/

Participe de comunidades de desenvolvedores:

Fiquem a vontade para me adicionar ao linkedin.

E também para me seguir no https://github.com/toticavalcanti.

Código final da aula:

https://github.com/toticavalcanti

Canais do Youtube

Toti

Lofi Music Zone Beats

Backing Track / Play-Along

Código Fluente

Putz!

Vocal Techniques and Exercises

PIX para doações

PIX Nubank

PIX Nubank


 

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

  1. 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.
  2. 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.
  3. 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.
  4. 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. 😉

Página principal do blog

Todas as aulas desse curso

Aula 38                        Aula 40

Meus links de afiliados:

Hostinger

Digital Ocean

One.com

Obrigado e bons estudos. 😉

About The Author
-

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>