本文介绍了如何使用 Go 语言和Token桶算法构建一个速率限制器,并将其与 Redis 集成以实现生产级别。文章详细讲解了速率限制的概念、Token Bucket 算法的原理和实现,以及如何使用 Redis 解决并发、无状态、多服务器同步和水平扩展等关键问题。
在暴露公共端点的系统中——无论是 Web3 API、交易平台、你的服务器,还是任何高吞吐量后端——速率限制都是一个至关重要的组件。它可以保护你的基础设施免受滥用,确保客户端之间的公平使用,并防御拒绝服务式行为。
在这篇文章中,我将介绍我们如何使用 Go 和 Token桶算法构建一个速率限制器,然后通过集成 Redis 将其进一步提升到生产级别。你将看到我们如何使用诸如 TTL、原子操作和 Lua 脚本等 Redis 功能来应对诸如并发、无状态、多服务器同步和水平扩展等关键挑战。
无论你是在构建去中心化应用后端还是扩展 API 服务器,这种方法都应该为你提供一个干净且高性能的流量控制基础。
速率限制限制了用户或系统在给定时间内可以执行的操作数量。它对于以下方面很有用:
根据用例和问题陈述,业界使用了几种流行的速率限制算法。
📖 要更深入地了解上述应用程序和工作原理,请参阅这篇必读的速率限制概述文章。
我们选择 Token桶算法 的原因如下:
一个简单的带内存存储的速率限制器——
type TokenBucket struct {
Tokens int
Capacity int
RefillRate int
LastRefill time.Time
RefillPeriod time.Duration
mu sync.Mutex
}
func NewTokenBucket(capacity, refillRate int, refillPeriod time.Duration) *TokenBucket {
return &TokenBucket{
Capacity: capacity,
Tokens: capacity,
RefillRate: refillRate,
RefillPeriod: refillPeriod,
LastRefill: time.Now(),
}
}
此结构会为每个用户初始化一个桶。它支持:
sync.Mutex
的并发安全。type RateLimiter struct {
Users map[string]*TokenBucket
Capacity int
Window time.Duration
mu sync.Mutex
}
func (rl *RateLimiter) CheckStatus(user string) bool {
rl.mu.Lock()
bucket, exists := rl.Users[user]
if !exists {
bucket = NewTokenBucket(rl.Capacity, rl.Capacity, rl.Window)
rl.Users[user] = bucket
}
rl.mu.Unlock()
bucket.mu.Lock()
defer bucket.mu.Unlock()
now := time.Now()
if now.Sub(bucket.LastRefill) >= bucket.RefillPeriod {
bucket.Tokens = bucket.Capacity
bucket.LastRefill = now
}
return bucket.Tokens > 0
}
func (rl *RateLimiter) IncrementTokenUsed(user string) {
rl.mu.Lock()
bucket, _ := rl.Users[user]
rl.mu.Unlock()
bucket.mu.Lock()
defer bucket.mu.Unlock()
now := time.Now()
if now.Sub(bucket.LastRefill) >= bucket.RefillPeriod {
bucket.Tokens = bucket.Capacity
bucket.LastRefill = now
}
if bucket.Tokens > 0 {
bucket.Tokens--
}
}
RateLimiter
结构有助于维护每个用户的Token桶。它包括诸如检查Token可用性(checkTokenStatus
)和更新使用情况(incrementToken
)之类的核心功能。这是一个基本的内存中实现,也通过锁支持并发。根据用例,你可以轻松地扩展此模型以支持用于不同 API 密钥或端点的多个速率限制器。
但是,这种内存方法存在一些关键限制:
为了克服这些问题,我们引入了 Redis:
为了进一步提高可扩展性并减少延迟,我们还可以将负载均衡器(配置了 IP 哈希)放置在服务器前面。这可确保来自给定用户的请求始终命中同一台服务器,从而可以使用本地 Redis 集群而不是单个中心化 Redis——从而获得更好的性能和水平扩展。
你可以尝试许多组件来找到最适合你系统的组件。在 Redis 中,你可以实现 Redis 集群 以进行横向扩展并提高可用性——但是在使用 Lua 脚本 时必须小心,因为它们在集群环境中未得到完全支持。你也可以尝试使用 Redlock 算法,尽管它增加了复杂性并引入了一些延迟。或者,你可以考虑使用 API 网关,它通常带有内置的速率限制功能。
如果需要,你还可以集成诸如 Prometheus 和 Grafana 之类的监控工具来跟踪指标并为任何滥用行为设置警报——就像 Binance、MEXC 等平台所做的那样,以防止用户压垮他们的系统。
- 原文链接: blog.blockmagnates.com/b...
- 登链社区 AI 助手,为大家转译优秀英文文章,如有翻译不通的地方,还请包涵~
如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!