[英]Limit Max Number of Requests Per Hour with `didip/tollbooth`
I'm new to rate limiting and want to use tollbooth to limit HTTP requests.我是速率限制的新手,想使用收费站来限制 HTTP 请求。
I also read the Token Bucket Algorithm page on Wikipedia.我还阅读了 Wikipedia 上的令牌桶算法页面。
For a simple test app, I want to limit the max number of concurrent requests to 10
regardless of request IP, and have a max burst size of 3
based on request IP.对于一个简单的测试应用程序,我想将最大并发请求数限制为
10
,而不管请求 IP,并且根据请求 IP,最大突发大小为3
。
NOTE: The 10
and 3
are just to make rate limiting easier to observe.注意:
10
和3
只是为了使速率限制更易于观察。
Below is my code based on the examples on tollbooth
's GitHub page :以下是我基于
tollbooth
的GitHub 页面上的示例的代码:
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!"))
}
I test the code by running curl -i localhost:8080
several times in rapid succession, and I do get HTTP/1.1 429 Too Many Requests
errors whenever I exceed the rate limit I set.我通过快速连续多次运行
curl -i localhost:8080
来测试代码,每当我超过我设置的速率限制时,我都会收到HTTP/1.1 429 Too Many Requests
错误。
Below are my questions:以下是我的问题:
How do I use tollbooth
to limit max number of concurrent requests to something like 10
?如何使用
tollbooth
将最大并发请求数限制为10
? And does it even make sense to do so?这样做是否有意义? I assume it does because rate limiting based only on IPs sounds like the server could still go out of memory when too many IPs access it at once.
我认为确实如此,因为仅基于 IP 的速率限制听起来像服务器仍然可以 go 超出 memory 当太多 IP 一次访问它时。
Am I approaching rate limiting correctly, or am I missing something?我是否正确接近速率限制,或者我错过了什么? Perhaps this is something that's better handled by whatever load balancer is working with the app in the cloud?
也许这是由任何负载均衡器与云中的应用程序一起工作的更好处理的事情?
UPDATE: Here's my working code based on Woody1193 's answer:更新:这是我基于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))
}
It appears that tollbooth doesn't offer the functionality you're looking for.收费亭似乎不提供您正在寻找的功能。 However, you can roll your own:
但是,您可以自己滚动:
type ConcurrentLimiter struct {
max int
current int
mut sync.Mutex
}
func NewConcurrentLimiter(limit int) *ConcurrentLimiter {
return &ConcurrentLimiter {
max: limit,
mut: new(sync.Mutex),
}
}
func (limiter *ConcurrentLimiter) LimitConcurrentRequests(lmt *limiter.Limiter,
next http.Handler) http.Handler {
middle := func(w http.ResponseWriter, r *http.Request) {
limiter.mut.Lock()
maxHit := limiter.current == limiter.max
if maxHit {
limiter.mut.Unlock()
httpError := // Insert your HTTP error here
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.
next.ServeHTTP(w, r)
}
return tollbooth.LimitHandler(lmt, http.HandlerFunc(middle))
}
Then, in your setup you can do:然后,在您的设置中,您可以执行以下操作:
http.Handle("/", NewConcurrentLimiter(10).LimitConcurrentRequests(HelloHandler))
This code works by maintaining a value describing how many requests the API is currently handling and returning an error if the maximum value has been met.此代码通过维护一个值来工作,该值描述 API 当前正在处理多少请求,如果已达到最大值,则返回错误。 The
Mutex
is used to ensure that the value is updated regardless of concurrent requests. Mutex
用于确保无论并发请求如何都更新该值。
I had to inject the tollbooth.Limiter
into the limiter I wrote because of the way tollbooth handles such functions (ie it doesn't operate as a middleware).由于 tollbooth 处理此类功能的方式(即它不作为中间件运行),我不得不将
tollbooth.Limiter
注入到我编写的限制器中。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.