1
0
Fork 0

Add period for rate limiter middleware

This commit is contained in:
mpl 2020-01-08 11:44:04 +01:00 committed by Traefiker Bot
parent 377c219fd9
commit 6f4aefffe7
12 changed files with 197 additions and 21 deletions

View file

@ -29,7 +29,7 @@ type rateLimiter struct {
rate rate.Limit // reqs/s
burst int64
// maxDelay is the maximum duration we're willing to wait for a bucket reservation to become effective, in nanoseconds.
// For now it is somewhat arbitrarily set to 1/rate.
// For now it is somewhat arbitrarily set to 1/(2*rate).
maxDelay time.Duration
sourceMatcher utils.SourceExtractor
next http.Handler
@ -61,20 +61,34 @@ func New(ctx context.Context, next http.Handler, config dynamic.RateLimit, name
}
burst := config.Burst
if burst <= 0 {
if burst < 1 {
burst = 1
}
// Logically, we should set maxDelay to ~infinity when config.Average == 0 (because it means to rate limiting),
period := time.Duration(config.Period)
if period == 0 {
period = time.Second
}
// Logically, we should set maxDelay to infinity when config.Average == 0 (because it means no rate limiting),
// but since the reservation will give us a delay = 0 anyway in this case, we're good even with any maxDelay >= 0.
var maxDelay time.Duration
if config.Average != 0 {
maxDelay = time.Second / time.Duration(config.Average*2)
var rtl float64
if config.Average > 0 {
rtl = float64(config.Average*int64(time.Second)) / float64(period)
// maxDelay does not scale well for rates below 1,
// so we just cap it to the corresponding value, i.e. 0.5s, in order to keep the effective rate predictable.
// One alternative would be to switch to a no-reservation mode (Allow() method) whenever we are in such a low rate regime.
if rtl < 1 {
maxDelay = 500 * time.Millisecond
} else {
maxDelay = time.Second / (time.Duration(rtl) * 2)
}
}
return &rateLimiter{
name: name,
rate: rate.Limit(config.Average),
rate: rate.Limit(rtl),
burst: burst,
maxDelay: maxDelay,
next: next,