dev 分支暂存

This commit is contained in:
2026-05-16 08:26:56 +08:00
parent 58c8caa570
commit eb4129176c
71 changed files with 8474 additions and 214 deletions
@@ -0,0 +1,102 @@
package middleware
import (
"net/http"
"sync"
"time"
"github.com/gin-gonic/gin"
)
// RateLimiter 基于内存令牌桶的限流中间件
// MVP阶段使用内存实现,后续可迁移到Redis
type RateLimiter struct {
mu sync.Mutex
buckets map[string]*tokenBucket
rate float64 // 每秒生成的令牌数
burst int // 桶容量
}
type tokenBucket struct {
tokens float64
lastTime time.Time
}
// NewRateLimiter 创建限流器
func NewRateLimiter(rate float64, burst int) *RateLimiter {
rl := &RateLimiter{
buckets: make(map[string]*tokenBucket),
rate: rate,
burst: burst,
}
// 定期清理过期桶
go rl.cleanup()
return rl
}
// Handler 返回Gin中间件
func (rl *RateLimiter) Handler() gin.HandlerFunc {
return func(c *gin.Context) {
key := c.ClientIP() // 按IP限流
if !rl.allow(key) {
c.JSON(http.StatusTooManyRequests, gin.H{
"error": "请求过于频繁,请稍后再试",
})
c.Abort()
return
}
c.Next()
}
}
func (rl *RateLimiter) allow(key string) bool {
rl.mu.Lock()
defer rl.mu.Unlock()
bucket, ok := rl.buckets[key]
now := time.Now()
if !ok {
rl.buckets[key] = &tokenBucket{
tokens: float64(rl.burst) - 1,
lastTime: now,
}
return true
}
// 补充令牌
elapsed := now.Sub(bucket.lastTime).Seconds()
bucket.tokens += elapsed * rl.rate
if bucket.tokens > float64(rl.burst) {
bucket.tokens = float64(rl.burst)
}
bucket.lastTime = now
// 消耗令牌
if bucket.tokens >= 1 {
bucket.tokens--
return true
}
return false
}
// cleanup 定期清理长时间未使用的桶
func (rl *RateLimiter) cleanup() {
for {
time.Sleep(5 * time.Minute)
rl.mu.Lock()
cutoff := time.Now().Add(-10 * time.Minute)
for key, bucket := range rl.buckets {
if bucket.lastTime.Before(cutoff) {
delete(rl.buckets, key)
}
}
rl.mu.Unlock()
}
}