文章精选推荐
1 JetBrains Ai assistant 编程工具让你的工作效率翻倍
2 Extra Icons:JetBrains IDE的图标增强神器
3 IDEA插件推荐-SequenceDiagram,自动生成时序图
4 BashSupport Pro 这个ides插件主要是用来干嘛的 ?
5 IDEA必装的插件:Spring Boot Helper的使用与功能特点
6 Ai assistant ,又是一个写代码神器
文章正文
要实现一个限制用户在 1 分钟内最多请求 1000 次的功能,可以使用一种常见的限流策略:令牌桶(Token Bucket) 或 漏桶算法(Leaky Bucket),也可以通过简单的 滑动窗口计数 来实现。这里我们可以使用一个更简单的方式:计数器 + 时间窗口。
1. 基本思路
我们可以在每个请求时检查当前时间窗口内的请求次数。每当用户发起请求时,我们:
- 检查当前时间与上次请求的时间间隔是否超过 1 分钟。
- 如果超过 1 分钟,则重置计数器(即将请求次数清零)。
- 如果当前时间窗口内的请求次数小于等于 1000,则允许请求并增加计数。
- 如果请求次数超过 1000 次,则拒绝请求,返回限流提示。
这种方式非常适合用来限制每个用户的请求频率。
2. 代码实现
我们可以使用一个 map
来记录每个用户的请求计数和时间戳。为了保证并发安全,通常需要使用 sync.Mutex
或者 sync.RWMutex
来保护共享数据。
package main
import (
"fmt"
"sync"
"time"
)
// 用户请求信息结构体
type UserRequest struct {
count int // 当前时间窗口内的请求次数
lastTime time.Time // 上次请求时间
}
// 限流器
type RateLimiter struct {
mu sync.Mutex
userLimit map[string]*UserRequest
interval time.Duration // 限流的时间窗口,1分钟
maxRequests int // 限制的最大请求次数
}
// 新建 RateLimiter
func NewRateLimiter(interval time.Duration, maxRequests int) *RateLimiter {
return &RateLimiter{
userLimit: make(map[string]*UserRequest),
interval: interval,
maxRequests: maxRequests,
}
}
// 检查用户请求是否被允许
func (rl *RateLimiter) AllowRequest(userID string) bool {
rl.mu.Lock()
defer rl.mu.Unlock()
// 获取当前时间
now := time.Now()
// 如果该用户第一次请求,初始化其请求信息
if _, exists := rl.userLimit[userID]; !exists {
rl.userLimit[userID] = &UserRequest{
count: 1,
lastTime: now,
}
return true
}
// 获取该用户的请求数据
user := rl.userLimit[userID]
// 计算时间差
timeDiff := now.Sub(user.lastTime)
// 如果超过了设定的时间窗口(例如 1 分钟),重置计数
if timeDiff >= rl.interval {
user.count = 1
user.lastTime = now
return true
}
// 如果请求次数小于最大限制,允许请求
if user.count < rl.maxRequests {
user.count++
return true
}
// 如果请求次数已超出最大限制,拒绝请求
return false
}
func main() {
// 创建一个限流器,限制每个用户 1 分钟内最多请求 1000 次
limiter := NewRateLimiter(1*time.Minute, 1000)
// 模拟多个请求
userID := "user1"
// 模拟用户连续请求
for i := 1; i <= 1100; i++ {
if limiter.AllowRequest(userID) {
fmt.Printf("Request %d for %s is allowed\n", i, userID)
} else {
fmt.Printf("Request %d for %s is denied (rate limit exceeded)\n", i, userID)
}
// 模拟请求间隔,避免过于频繁地触发限流
time.Sleep(50 * time.Millisecond) // 50ms 请求间隔
}
}
3. 代码解释
-
RateLimiter 结构体:这是一个限流器,负责存储用户的请求数据并检查是否符合限流规则。
userLimit
:这是一个map
,键是userID
(用户的唯一标识),值是UserRequest
(记录该用户的请求计数和上次请求的时间戳)。interval
:限制时间窗口,这里是 1 分钟。maxRequests
:每个用户在指定时间窗口内允许的最大请求次数,这里是 1000 次。
-
AllowRequest 方法:该方法检查用户是否超过请求限制。
- 如果当前时间距离上次请求已经超过了 1 分钟,则重置计数器。
- 如果请求次数还未超过 1000 次,允许请求并增加计数。
- 如果请求次数超过了限制,则拒绝请求。
-
模拟请求:
main
函数模拟了一个用户连续发起请求的场景,每发起一次请求都会调用AllowRequest
方法检查是否符合限流规则。如果符合规则,允许请求,否则拒绝。
4. 并发安全
在实际应用中,尤其是分布式系统中,通常需要处理并发请求。为了保证对 userLimit
这个共享资源的访问安全,我们使用了 sync.Mutex
来确保在同一时间只有一个 goroutine 能够操作限流数据。这避免了在多并发情况下数据的不一致问题。
在高并发场景下,如果需要提高性能,可以考虑使用更细粒度的锁(如 sync.RWMutex
)或者在分布式环境下使用 Redis 等外部存储来处理限流。
5. 高效的改进方式
如果对性能有更高的要求,可以考虑使用更高效的数据结构,比如:
- 滑动时间窗口:通过存储每次请求的时间戳,可以更加精细地控制每个用户在某个时间窗口内的请求次数。
- Redis 限流:在分布式环境下,可以通过 Redis 实现更高效的限流,因为 Redis 提供了原子操作和过期时间机制,适合处理高并发的限流场景。
总结
通过上述的 计数器 + 时间窗口
方式,我们成功实现了对每个用户 1 分钟内最多请求 1000 次的限流功能。这个方案简单且易于理解,适用于大多数场景。在实际生产中,可能需要进一步优化限流策略,结合分布式存储和高效的算法来应对更高的并发需求。