fix(limiter/rate): short-circuit Allow on denial, add missing httpLoader.Close, doc comments, tests

- limiterGroup.Allow(): return false immediately on first denial instead of
  continuing through all limiters (wasting tokens on subsequent limiters).
  Sort limiters by Limit() so most restrictive fires first.
- rateLimiter.Close(): add missing httpLoader.Close() to prevent resource leak.
- Add doc comments for all exported symbols (package, constants, Options,
  NewRateLimiter, NewLimiter, ErrRateLimit, generators).
- Add rate_test.go with 59 unit tests covering generators, limiter groups,
  parsing (limit/line/patterns), reload, loader merge, Close behavior,
  and priority ordering (exact IP > CIDR > $$ > $).
This commit is contained in:
ginuerzh
2026-05-24 20:09:26 +08:00
parent 05fe690f67
commit 9bbd7da526
4 changed files with 903 additions and 11 deletions
+8 -8
View File
@@ -9,14 +9,15 @@ import (
"golang.org/x/time/rate"
)
var (
ErrRateLimit = errors.New("rate limit")
)
// ErrRateLimit is returned when a rate limit is exceeded.
var ErrRateLimit = errors.New("rate limit")
type rlimiter struct {
limiter *rate.Limiter
}
// NewLimiter creates a [limiter.Limiter] with the specified rate r (events per second)
// and burst b.
func NewLimiter(r float64, b int) limiter.Limiter {
return &rlimiter{
limiter: rate.NewLimiter(rate.Limit(r), b),
@@ -42,14 +43,13 @@ func newLimiterGroup(limiters ...limiter.Limiter) *limiterGroup {
return &limiterGroup{limiters: limiters}
}
func (l *limiterGroup) Allow(n int) (b bool) {
b = true
func (l *limiterGroup) Allow(n int) bool {
for i := range l.limiters {
if v := l.limiters[i].Allow(n); !v {
b = false
if !l.limiters[i].Allow(n) {
return false
}
}
return
return true
}
func (l *limiterGroup) Limit() float64 {