Go by Example - IT: Rate Limiting

Il Rate limiting è un’importante tecnica per controllare l’uso delle risorse e mantenere un’alta qualità di servizio. Go supporta elegantemente il rate limiting tramite le goroutine, i channel e i ticker.

package main
import "time"
import "fmt"
func main() {

Come prima cosa vedremo un semplice esempio di rate limiting. Supponiamo di dover limitare il numero di richieste (per esempio in un server HTTP). Utilizzeremo un channel per mimare l’entrata delle richieste.

    richieste := make(chan int, 5)
    for i := 1; i <= 5; i++ {
        richieste <- i
    }
    close(richieste)

Questo channel limitatore riceverà un valore ogni 200 millisecondi. Fungerà da regolatore nel nostro schema di rate limiting.

    limitatore := time.Tick(time.Millisecond * 200)

Bloccando l’esecuzione su una ricezione da limitatore prima di servire ogni richiesta, ci limitiamo a fare al massimo una richiesta ogni 200 millisecondi.

    for ric := range richieste {
        <-limitatore
        fmt.Println("richiesta", ric, time.Now())
    }

Potremmo aver bisogno di sostenere brevi “raffiche” di richieste, mantenendo comunque il rate limiting. Possiamo fare ciò mettendo bufferizzando il nostro channel. Questo channel limitatoreARaffica ci permetterà di sostenere raffiche fino ad un massimo di 3 richieste.

    limitatoreARaffica := make(chan time.Time, 3)

Riempiamo il channel per dire che già dall’inizio possiamo prendere in carico 3 richieste.

    for i := 0; i < 3; i++ {
        limitatoreARaffica <- time.Now()
    }

Ogni 200 millisecondi proveremo ad inviare un nuovo valore al limitatoreARaffica, fino ad arrivare al suo limite di 3 richieste.

    go func() {
        for t := range time.Tick(time.Millisecond * 200) {
            limitatoreARaffica <- t
        }
    }()

Ora simuleremo 5 richieste al nostro limitatore. Le prime tre potranno godere della capacità del limitatore di eseguire richieste a raffica.

    richiesteARaffica := make(chan int, 5)
    for i := 1; i <= 5; i++ {
        richiesteARaffica <- i
    }
    close(richiesteARaffica)
    for ric := range richiesteARaffica {
        <-limitatoreARaffica
        fmt.Println("richiesta", ric, time.Now())
    }
}

Eseguendo il nostro programma vedremo il gruppo di richieste che verranno gestite ognuna ogni 200 millisecondi.

$ go run rate-limiting.go
richiesta 1 2016-04-17 19:05:37.736132953 +0200 CEST
richiesta 2 2016-04-17 19:05:37.936138961 +0200 CEST
richiesta 3 2016-04-17 19:05:38.136209943 +0200 CEST
richiesta 4 2016-04-17 19:05:38.336145582 +0200 CEST
richiesta 5 2016-04-17 19:05:38.536120745 +0200 CEST

Invece, per il nostro secondo gruppo di richieste vedremo che le prime tre vengono eseguite all’istante, mentre le altre 2 a distanza di 200 millisecondi l’una dall’altra.

richiesta 1 2016-04-17 19:05:38.536251527 +0200 CEST
richiesta 2 2016-04-17 19:05:38.536266185 +0200 CEST
richiesta 3 2016-04-17 19:05:38.536277665 +0200 CEST
richiesta 4 2016-04-17 19:05:38.736385724 +0200 CEST
richiesta 5 2016-04-17 19:05:38.936385957 +0200 CEST

Prossimo esempio: Contatori Atomici.