您好,登錄后才能下訂單哦!
小編給大家分享一下Go如何基于IP限制HTTP訪問頻率,相信大部分人都還不怎么了解,因此分享這篇文章給大家參考一下,希望大家閱讀完這篇文章后大有收獲,下面讓我們一起去了解一下吧!
創建一個簡單的中間件實現基于 IP 限制 HTTP 訪問頻率。
簡單的 HTTP 服務
讓我們從創建一個簡單的 HTTP 服務開始,它有非常簡單的終端。 但是,因為它的訪問頻率可能非常高,因此我們要為它添加頻率限制。
package main import ( "log" "net/http" ) func main() { mux := http.NewServeMux() mux.HandleFunc("/", okHandler) if err := http.ListenAndServe(":8888", mux); err != nil { log.Fatalf("unable to start server: %s", err.Error()) } } func okHandler(w http.ResponseWriter, r *http.Request) { // 某些消耗很高的數據庫請求 w.Write([]byte("alles gut")) }
通過 main.go 我們啟動服務,監聽 :8888 端口,這樣我們就有了一個簡單的終端 /。
golang.org/x/time/rate
我們將使用名為 x/time/rate 的 Go 包,它提供了一個令牌桶速率限制器算法。rate#Limiter 控制允許事件發生的頻率。它實現了一個大小為 b 的「令牌桶」,最初是滿的,并以每秒 r 的速度重新填充令牌。通俗地講,就是在任何足夠大的時間間隔內,限制器將速率限制為每秒 r 個令牌,最大突發大小為 b 個事件。
由于我們希望實現每個 IP 地址的速率限制器,我們還需要維護一個限制器映射。
package main import ( "sync" "golang.org/x/time/rate" ) // IPRateLimiter . type IPRateLimiter struct { ips map[string]*rate.Limiter mu *sync.RWMutex r rate.Limit b int } // NewIPRateLimiter . func NewIPRateLimiter(r rate.Limit, b int) *IPRateLimiter { i := &IPRateLimiter{ ips: make(map[string]*rate.Limiter), mu: &sync.RWMutex{}, r: r, b: b, } return i } // AddIP 創建了一個新的速率限制器,并將其添加到 ips 映射中, // 使用 IP地址作為密鑰 func (i *IPRateLimiter) AddIP(ip string) *rate.Limiter { i.mu.Lock() defer i.mu.Unlock() limiter := rate.NewLimiter(i.r, i.b) i.ips[ip] = limiter return limiter } // GetLimiter 返回所提供的IP地址的速率限制器(如果存在的話). // 否則調用 AddIP 將 IP 地址添加到映射中 func (i *IPRateLimiter) GetLimiter(ip string) *rate.Limiter { i.mu.Lock() limiter, exists := i.ips[ip] if !exists { i.mu.Unlock() return i.AddIP(ip) } i.mu.Unlock() return limiter }
NewIPRateLimiter 創建一個 IP 限制器實例,HTTP 服務器必須調用這個實例的 GetLimiter 來獲得指定 IP 的限制器 (從映射或生成一個新的)。
中間件
讓我們升級的 HTTP 服務并將中間件添加到所有端點,如果 IP 達到限制,它將響應 429 Too Many Requests,否則,它將繼續該請求。
每一個經過中間件的請求,我們都會調用 limitMiddleware 函數中的全局方法 Allow()。如果存儲桶中沒有令牌了,該方法會返回 false,該請求會收到 429 Too Many Requests 的響應。否則 Allow() 方法將消耗一個令牌,并將請求傳遞給下一個程序。
package main import ( "log" "net/http" ) var limiter = NewIPRateLimiter(1, 5) func main() { mux := http.NewServeMux() mux.HandleFunc("/", okHandler) if err := http.ListenAndServe(":8888", limitMiddleware(mux)); err != nil { log.Fatalf("unable to start server: %s", err.Error()) } } func limitMiddleware(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { limiter := limiter.GetLimiter(r.RemoteAddr) if !limiter.Allow() { http.Error(w, http.StatusText(http.StatusTooManyRequests), http.StatusTooManyRequests) return } next.ServeHTTP(w, r) }) } func okHandler(w http.ResponseWriter, r *http.Request) { // 非常重要的數據請求(譯者注:這句話沒理解到位) w.Write([]byte("alles gut")) }
編譯 & 執行
go get golang.org/x/time/rate go build -o server . ./server
測試
這是我喜歡使用的一個非常好的來進行 HTTP 負載測試的工具,它叫做 vegeta (它也是用 Go 編寫的)。
brew install vegeta
我們需要創建一個簡單的配置文件,來展示我們希望生成的請求。
GET http://localhost:8888/
然后運行攻擊 10 秒,每個時間單位 100 個請求。
vegeta attack -duration=10s -rate=100 -targets=vegeta.conf | vegeta report
結果,您將看到一些請求返回了 200,但是大多數都返回了 429。
以上是“Go如何基于IP限制HTTP訪問頻率”這篇文章的所有內容,感謝各位的閱讀!相信大家都有了一定的了解,希望分享的內容對大家有所幫助,如果還想學習更多知識,歡迎關注億速云行業資訊頻道!
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。