隨著網路和行動網路的快速發展,伺服器面對的壓力越來越大,如何限制客戶端對伺服器的請求,避免伺服器崩潰成為了一個大問題。在實際專案中,我們經常需要限制IP的請求數,保證網站的可用性。
在這裡,我們將介紹如何使用Golang實作IP限流。總的來說,我們將利用令牌桶演算法來實現基於IP位址的限流。令牌桶演算法是一種流量控制演算法,它允許一定數量的請求在一定時間內通過並限制一段時間內的請求流量。
實現細節
令牌桶演算法以恆定速率往桶中放入令牌,令牌桶有一個容量限制,表示桶中的令牌數量不會超過容量。對於每個請求,從桶中移除一個令牌,如果桶中沒有令牌,則請求無法通過。
為了實現基於IP位址的限流,我們需要為每個IP位址建立一個令牌桶,每個令牌桶都有一個最大容量和一個恆定的速度。當請求到來時,我們從令牌桶中移除一個令牌,如果桶中沒有令牌,則拒絕請求。
基於此,我們可以定義一個IP限流器:
type IPRateLimiter struct { limiterBuckets map[string]*rate.Limiter mu *sync.Mutex r rate.Limit b int }
其中,limiterBuckets 是一個映射,將字串 IP 位址映射到令牌桶。 mu 是互斥鎖, r 是速率限制器每秒放入令牌的速率, b 是令牌桶的容量。
為了為每個IP位址建立令牌桶,我們定義一個函數NewIPRateLimiter :
func NewIPRateLimiter(r rate.Limit, b int) *IPRateLimiter { return &IPRateLimiter{ limiterBuckets: make(map[string]*rate.Limiter), mu: &sync.Mutex{}, r: r, b: b, } } func (i *IPRateLimiter) AddIP(ip string) *rate.Limiter { i.mu.Lock() defer i.mu.Unlock() limiter := rate.NewLimiter(i.r, i.b) i.limiterBuckets[ip] = limiter return limiter }
AddIP函數用於為IP位址建立令牌桶。如果為該IP位址建立了令牌桶,則傳回現有的令牌桶,否則建立一個新的令牌桶並傳回。
最後,我們可以實作HTTP中間件來限制IP位址的請求數:
func (i *IPRateLimiter) Limit(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { ip := r.RemoteAddr limiter, ok := i.limiterBuckets[ip] if !ok { limiter = i.AddIP(ip) } if !limiter.Allow() { http.Error(w, http.StatusText(http.StatusTooManyRequests), http.StatusTooManyRequests) return } next.ServeHTTP(w, r) }) }
該中間件允許在指定的速率和容量下通過請求。如果請求數超過容量,則傳回HTTP錯誤代碼429(太多請求)。
完整程式碼如下:
package main import ( "net/http" "strconv" "sync" "golang.org/x/time/rate" ) type IPRateLimiter struct { limiterBuckets map[string]*rate.Limiter mu *sync.Mutex r rate.Limit b int } func NewIPRateLimiter(r rate.Limit, b int) *IPRateLimiter { return &IPRateLimiter{ limiterBuckets: make(map[string]*rate.Limiter), mu: &sync.Mutex{}, r: r, b: b, } } func (i *IPRateLimiter) AddIP(ip string) *rate.Limiter { i.mu.Lock() defer i.mu.Unlock() limiter := rate.NewLimiter(i.r, i.b) i.limiterBuckets[ip] = limiter return limiter } func (i *IPRateLimiter) Limit(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { ip := r.RemoteAddr limiter, ok := i.limiterBuckets[ip] if !ok { limiter = i.AddIP(ip) } if !limiter.Allow() { http.Error(w, http.StatusText(http.StatusTooManyRequests), http.StatusTooManyRequests) return } next.ServeHTTP(w, r) }) } func IndexHandler(w http.ResponseWriter, r *http.Request) { w.Write([]byte("welcome.")) } func main() { limit := rate.Limit(10) // 速率,每秒放入令牌的数量 capacity := 100 // 容量,桶的大小 ipRateLimiter := NewIPRateLimiter(limit, capacity) http.Handle("/", ipRateLimiter.Limit(http.HandlerFunc(IndexHandler))) err := http.ListenAndServe(":8080", nil) if err != nil { panic(err) } }
在這個例子中,我們允許每秒放入10個令牌,並限製桶的容量為100。這表示此限制器可以處理每秒最多10個請求,但如果對相同IP位址的請求達到100個,則無法通過請求。同時,我們定義了一個簡單的處理程序,它將回應「歡迎」。
結論
在本文中,我們使用Golang實作了IP限流,採用令牌桶演算法來限制每個IP位址的請求速率。這種方法可以實作一個簡單而有效的限流機制,並且可以在Golang中方便地實作。當你在編寫高並發的網頁應用程式時,這可能是一個非常有用的技巧。
以上是Golang實現ip限流的詳細內容。更多資訊請關注PHP中文網其他相關文章!