速率限制是 Web 開發和 API 設計中的關鍵概念。它確保使用者或系統在特定時間範圍內只能向伺服器發出有限數量的請求。在這篇文章中,我們將探討什麼是速率限制、為什麼它很重要,以及如何在 Go 中實作一個簡單的速率限制器。
想像一個主題樂園,裡面有雲霄飛車,每 10 分鐘只能容納 10 人。如果超過 10 人試圖在該時間內上車,他們將不得不等待。這個類比反映了軟體系統中速率限制的原理。
用技術術語來說,速率限制限制客戶端(例如使用者、裝置或 IP 位址)在預定義時間內可以傳送到伺服器的請求數量。它有幫助:
例如,API 可能允許每位使用者每分鐘 100 個請求。如果使用者超出此限制,伺服器將拒絕進一步的請求,直到限制重置。
實現速率限制的常見方法是透過令牌桶演算法。其工作原理如下:
讓我們深入研究在 Go 中建立一個速率限制器,將每個客戶端限制為每分鐘 3 個請求。
我們將使用sync.Mutex來確保執行緒安全性並儲存令牌數量、最大容量和填充率等資訊。
package main import ( "sync" "time" ) type RateLimiter struct { tokens float64 // Current number of tokens maxTokens float64 // Maximum tokens allowed refillRate float64 // Tokens added per second lastRefillTime time.Time // Last time tokens were refilled mutex sync.Mutex } func NewRateLimiter(maxTokens, refillRate float64) *RateLimiter { return &RateLimiter{ tokens: maxTokens, maxTokens: maxTokens, refillRate: refillRate, lastRefillTime: time.Now(), } }
代幣應根據上次充值以來的經過時間定期補充。
func (r *RateLimiter) refillTokens() { now := time.Now() duration := now.Sub(r.lastRefillTime).Seconds() tokensToAdd := duration * r.refillRate r.tokens += tokensToAdd if r.tokens > r.maxTokens { r.tokens = r.maxTokens } r.lastRefillTime = now }
Allow 方法將根據可用令牌確定請求是否可以繼續。
func (r *RateLimiter) Allow() bool { r.mutex.Lock() defer r.mutex.Unlock() r.refillTokens() if r.tokens >= 1 { r.tokens-- return true } return false }
為了限制每個客戶端的請求,我們將建立 IP 位址到各自速率限制器的對應。
type IPRateLimiter struct { limiters map[string]*RateLimiter mutex sync.Mutex } func NewIPRateLimiter() *IPRateLimiter { return &IPRateLimiter{ limiters: make(map[string]*RateLimiter), } } func (i *IPRateLimiter) GetLimiter(ip string) *RateLimiter { i.mutex.Lock() defer i.mutex.Unlock() limiter, exists := i.limiters[ip] if !exists { // Allow 3 requests per minute limiter = NewRateLimiter(3, 0.05) i.limiters[ip] = limiter } return limiter }
最後,我們將建立一個 HTTP 中間件,對每個客戶端實作速率限制。
package main import ( "sync" "time" ) type RateLimiter struct { tokens float64 // Current number of tokens maxTokens float64 // Maximum tokens allowed refillRate float64 // Tokens added per second lastRefillTime time.Time // Last time tokens were refilled mutex sync.Mutex } func NewRateLimiter(maxTokens, refillRate float64) *RateLimiter { return &RateLimiter{ tokens: maxTokens, maxTokens: maxTokens, refillRate: refillRate, lastRefillTime: time.Now(), } }
以下是如何將它們連接在一起並測試速率限制器。
func (r *RateLimiter) refillTokens() { now := time.Now() duration := now.Sub(r.lastRefillTime).Seconds() tokensToAdd := duration * r.refillRate r.tokens += tokensToAdd if r.tokens > r.maxTokens { r.tokens = r.maxTokens } r.lastRefillTime = now }
啟動伺服器並使用curl或瀏覽器測試:
func (r *RateLimiter) Allow() bool { r.mutex.Lock() defer r.mutex.Unlock() r.refillTokens() if r.tokens >= 1 { r.tokens-- return true } return false }
GitHub 儲存庫
以上是初學者的速率限制:它是什麼以及如何在 Go 中構建速率限制的詳細內容。更多資訊請關注PHP中文網其他相關文章!