Ограничиваем скорость операций в Golang
Для ограничения скорости операций в единицу времени в языке программирования Go можно использовать time.Ticker. Это неплохо работает для скоростей до нескольких десятков операций в секунду. Если же речь идет о более высоких скоростях, тогда лучше отдавать предпочтение ограничителю скорости сегмента токенов, например, Limiter из golang.org/x/time/rate.
import "time" const rateLimit = time.Second / 10 // 10 calls per second // Client это интерфейс, // который вызывает что-то с полезной нагрузкой. type Client interface { Call(*Payload) } // Payload это некоторая полезная нагрузка, // которую Client отправляет при вызове. type Payload struct {} // RateLimitCall ограничивает скорость клиентских вызовов полезной нагрузки. func RateLimitCall(client Client, payloads []*Payload) { throttle := time.Tick(rateLimit) for _, payload := range payloads { <-throttle // ограничение скорости клиентских вызовов go client.Call(payload) } }
Если же надо разрешить некоторые всплески, достаточно добавить буфер к дросселю:
import "time" const rateLimit = time.Second / 10 // 10 calls per second // Client это интерфейс, // который вызывает что-то с полезной нагрузкой. type Client interface { Call(*Payload) } // Payload это некоторая полезная нагрузка, // которую Client отправляет при вызове. type Payload struct {} // BurstRateLimitCall позволяет ограничивать // пакетную скорость клиентских вызовов полезной нагрузки. func BurstRateLimitCall(ctx context.Context, client Client, payloads []*Payload, burstLimit int) { throttle := make(chan time.Time, burstLimit) ctx, cancel := context.WithCancel(ctx) defer cancel() go func() { ticker := time.NewTicker(rateLimit) defer ticker.Stop() for t := range ticker.C { select { case throttle <- t: case <-ctx.Done(): return // выходим из горутины, когда окружающая функция завершается } } }() for _, payload := range payloads { <-throttle // ограничение скорости клиентских вызовов go client.Call(payload) } }
По материалам блога https://golang-blog.blogspot.com/.