我不熟悉速率限制,我想使用tollbooth来限制HTTP请求.
我还读了维基百科上Token Bucket 算法rithm页的内容.
对于一个简单的测试应用程序,我希望将并发请求的最大数量限制为10
,而不考虑请求IP,并基于请求IP将最大突发大小限制为3
.
注:10
和3
只是为了使速率限制更易于观察.
以下是我基于tollbooth
的GitHub page的例子编写的代码:
package main
import (
"net/http"
"time"
"github.com/didip/tollbooth/v7"
"github.com/didip/tollbooth/v7/limiter"
)
func main() {
lmt := tollbooth.NewLimiter(3, &limiter.ExpirableOptions{DefaultExpirationTTL: time.Hour})
http.Handle("/", tollbooth.LimitFuncHandler(lmt, HelloHandler))
http.ListenAndServe(":8080", nil)
}
func HelloHandler(w http.ResponseWriter, req *http.Request) {
w.Write([]byte("Hello, World!"))
}
我通过快速连续运行curl -i localhost:8080
几次来测试代码,每当我超过设置的速率限制时,我都会收到HTTP/1.1 429 Too Many Requests
个错误.
以下是我的问题:
-
如何使用
tollbooth
将并发请求的最大数量限制在10
左右?这样做有意义吗?我认为是这样的,因为仅基于IP的速率限制听起来像是当太多IP同时访问服务器时,服务器可能仍然会耗尽内存. -
我是否正确地达到了速率限制,或者我错过了什么?也许这是更好地由任何负载均衡器在云中使用应用程序来处理的事情?
根据Woody1193的答案,我的工作代码如下:
package main
import (
"net/http"
"sync"
"time"
"github.com/didip/tollbooth/v7"
"github.com/didip/tollbooth/v7/limiter"
)
func main() {
ipLimiter := tollbooth.NewLimiter(3, &limiter.ExpirableOptions{DefaultExpirationTTL: time.Hour})
globalLimiter := NewConcurrentLimiter(10)
http.Handle("/", globalLimiter.LimitConcurrentRequests(ipLimiter, HelloHandler))
http.ListenAndServe(":8080", nil)
}
func HelloHandler(w http.ResponseWriter, req *http.Request) {
w.Write([]byte("Hello, World!"))
}
type ConcurrentLimiter struct {
max int
current int
mut sync.Mutex
}
func NewConcurrentLimiter(limit int) *ConcurrentLimiter {
return &ConcurrentLimiter{
max: limit,
}
}
func (limiter *ConcurrentLimiter) LimitConcurrentRequests(lmt *limiter.Limiter,
handler func(http.ResponseWriter, *http.Request)) http.Handler {
middle := func(w http.ResponseWriter, r *http.Request) {
limiter.mut.Lock()
maxHit := limiter.current == limiter.max
if maxHit {
limiter.mut.Unlock()
http.Error(w, http.StatusText(429), http.StatusTooManyRequests)
return
}
limiter.current += 1
limiter.mut.Unlock()
defer func() {
limiter.mut.Lock()
limiter.current -= 1
limiter.mut.Unlock()
}()
// There's no rate-limit error, serve the next handler.
handler(w, r)
}
return tollbooth.LimitHandler(lmt, http.HandlerFunc(middle))
}