@@ -3,6 +3,7 @@ package api
|
||||
import (
|
||||
"net/http"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
@@ -22,8 +23,9 @@ type ipRateLimiter struct {
|
||||
}
|
||||
|
||||
type ipEntry struct {
|
||||
limiter *rate.Limiter
|
||||
lastSeen time.Time
|
||||
limiter *rate.Limiter
|
||||
// lastSeen 是 UnixNano 时间戳;请求路径无锁写、回收循环无锁读
|
||||
lastSeen atomic.Int64
|
||||
}
|
||||
|
||||
func newIPRateLimiter() *ipRateLimiter {
|
||||
@@ -33,6 +35,7 @@ func newIPRateLimiter() *ipRateLimiter {
|
||||
}
|
||||
|
||||
// get 返回 ip 对应的令牌桶,参数与当前配置不一致时就地调整。
|
||||
// lastSeen 用原子时间戳:并发请求与回收循环两侧无锁读写,避免数据竞态。
|
||||
func (l *ipRateLimiter) get(ip string, rps, burst int) *rate.Limiter {
|
||||
l.mu.RLock()
|
||||
entry, ok := l.limiters[ip]
|
||||
@@ -45,7 +48,7 @@ func (l *ipRateLimiter) get(ip string, rps, burst int) *rate.Limiter {
|
||||
}
|
||||
l.mu.Unlock()
|
||||
}
|
||||
entry.lastSeen = time.Now()
|
||||
entry.lastSeen.Store(time.Now().UnixNano())
|
||||
if entry.limiter.Limit() != rate.Limit(rps) || entry.limiter.Burst() != burst {
|
||||
entry.limiter.SetLimit(rate.Limit(rps))
|
||||
entry.limiter.SetBurst(burst)
|
||||
@@ -58,9 +61,9 @@ func (l *ipRateLimiter) evictLoop() {
|
||||
defer ticker.Stop()
|
||||
for range ticker.C {
|
||||
l.mu.Lock()
|
||||
cutoff := time.Now().Add(-ipEvictInterval)
|
||||
cutoff := time.Now().Add(-ipEvictInterval).UnixNano()
|
||||
for ip, e := range l.limiters {
|
||||
if e.lastSeen.Before(cutoff) {
|
||||
if e.lastSeen.Load() < cutoff {
|
||||
delete(l.limiters, ip)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user