mirror of
https://github.com/VictoriaMetrics/VictoriaMetrics.git
synced 2024-12-01 14:47:38 +00:00
79 lines
1.8 KiB
Go
79 lines
1.8 KiB
Go
|
package ratelimiter
|
||
|
|
||
|
import (
|
||
|
"sync"
|
||
|
"time"
|
||
|
|
||
|
"github.com/VictoriaMetrics/VictoriaMetrics/lib/timerpool"
|
||
|
"github.com/VictoriaMetrics/metrics"
|
||
|
)
|
||
|
|
||
|
// RateLimiter limits per-second rate of arbitrary resources.
|
||
|
//
|
||
|
// Call Register() for registering the given amounts of resources.
|
||
|
type RateLimiter struct {
|
||
|
// perSecondLimit is the per-second limit of resources.
|
||
|
perSecondLimit int64
|
||
|
|
||
|
// stopCh is used for unbloking rate limiting.
|
||
|
stopCh <-chan struct{}
|
||
|
|
||
|
// mu protects budget and deadline from concurrent access.
|
||
|
mu sync.Mutex
|
||
|
|
||
|
// The current budget. It is increased by perSecondLimit every second.
|
||
|
budget int64
|
||
|
|
||
|
// The next deadline for increasing the budget by perSecondLimit.
|
||
|
deadline time.Time
|
||
|
|
||
|
// limitReached is a counter, which is increased every time the limit is reached.
|
||
|
limitReached *metrics.Counter
|
||
|
}
|
||
|
|
||
|
// New creates new rate limiter with the given perSecondLimit.
|
||
|
//
|
||
|
// stopCh is used for unblocking Register() calls when the rate limiter is no longer needed.
|
||
|
func New(perSecondLimit int64, limitReached *metrics.Counter, stopCh <-chan struct{}) *RateLimiter {
|
||
|
return &RateLimiter{
|
||
|
perSecondLimit: perSecondLimit,
|
||
|
stopCh: stopCh,
|
||
|
limitReached: limitReached,
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Register registers count resources.
|
||
|
//
|
||
|
// Register blocks if the given per-second rate limit is exceeded.
|
||
|
// It may be forcibly unblocked by closing stopCh passed to New().
|
||
|
func (rl *RateLimiter) Register(count int) {
|
||
|
if rl == nil {
|
||
|
return
|
||
|
}
|
||
|
|
||
|
limit := rl.perSecondLimit
|
||
|
if limit <= 0 {
|
||
|
return
|
||
|
}
|
||
|
|
||
|
rl.mu.Lock()
|
||
|
defer rl.mu.Unlock()
|
||
|
|
||
|
for rl.budget <= 0 {
|
||
|
if d := time.Until(rl.deadline); d > 0 {
|
||
|
rl.limitReached.Inc()
|
||
|
t := timerpool.Get(d)
|
||
|
select {
|
||
|
case <-rl.stopCh:
|
||
|
timerpool.Put(t)
|
||
|
return
|
||
|
case <-t.C:
|
||
|
timerpool.Put(t)
|
||
|
}
|
||
|
}
|
||
|
rl.budget += limit
|
||
|
rl.deadline = time.Now().Add(time.Second)
|
||
|
}
|
||
|
rl.budget -= int64(count)
|
||
|
}
|