optimized_go_tools/am_ratelimit/ratelimit.go

74 lines
2.1 KiB
Go
Raw Permalink Normal View History

2024-07-08 03:14:19 +00:00
package am_ratelimit
import (
"github.com/gin-gonic/gin"
"net/http"
"sync"
"time"
)
// 储存用户信息,包括最后一次访问时间和访问计数
type RequestInfo struct {
LastAccessTime time.Time
RequestCount int
}
// 互斥锁用于同步对共享资源如requestInfo映射的访问确保在多线程环境下的数据一致性
var mutex = &sync.Mutex{}
// 速率限制配置项结构体
type RateLimitConfig struct {
maxRequests int // 最大访问次数
timeWindow time.Duration // 窗口时间
requestInfo map[string]*RequestInfo // 用户信息
}
/*
* 初始化速率限制配置
* 参数
* 1. maxRequests int窗口时间内最大访问数量限制
* 2. timeWindow int限制的窗口时间单位为秒
*/
func NewRateLimitConfig(maxRequests int, timeWindow int) *RateLimitConfig {
return &RateLimitConfig{
maxRequests: maxRequests,
timeWindow: time.Duration(timeWindow) * time.Second,
requestInfo: make(map[string]*RequestInfo),
}
}
/*
* 速率限制中间件
*/
func (r1 *RateLimitConfig) RateLimitMiddleware(c *gin.Context) {
ip := c.ClientIP() // 获取用户IP
mutex.Lock() // 在访问共享资源前加锁,确保只有同一线程才能访问
defer mutex.Unlock() // 在数据返回前解锁
info, exists := r1.requestInfo[ip] // 检查requestInfo映射中是否已经存在该IP信息
// 如果该IP的请求信息不存在则初始化一个新的RequestInfo实例并将其添加到requestInfo映射中
if !exists {
r1.requestInfo[ip] = &RequestInfo{LastAccessTime: time.Now(), RequestCount: 1}
return
}
// 如果最后一次请求的时间已经超出了时间窗口,重置请求计数并更新最后一次访问时间
if time.Since(info.LastAccessTime) > r1.timeWindow {
info.RequestCount = 1
info.LastAccessTime = time.Now()
return
}
// 增加请求数并检查速率限制
info.RequestCount++
if info.RequestCount > r1.maxRequests {
c.JSON(http.StatusTooManyRequests, gin.H{"error": "Too many requests"})
c.Abort()
return
}
// 更新最后一次访问时间
info.LastAccessTime = time.Now()
}