英文:
Rate limit specific endpoints
问题
我是你的中文翻译助手,以下是你要翻译的内容:
我刚开始学习Go语言,并且正在开发我的第一个API。我有两个端点,我想对其中一个进行速率限制。我找到了一个有用的教程来帮助我入门,并且我基于这个教程的方法进行了开发,但我意识到这种方法会对我的两个端点都进行速率限制:
var limiter = rate.NewLimiter(rate.Every((1*time.Hour)/3), 1)
func limit(next http.Handler) http.Handler {
return http.HandlerFunc(func(res http.ResponseWriter, req *http.Request) {
if limiter.Allow() == false {
http.Error(res, http.StatusText(429), http.StatusTooManyRequests)
return
}
next.ServeHTTP(res, req)
})
}
func main() {
mux := http.NewServeMux()
mux.HandleFunc("/", createNewToken)
mux.HandleFunc("/notify", sendPushNotificationToAllTokens)
log.Fatal(http.ListenAndServeTLS(":5050", "localhost.crt", "localhost.key", limit(mux)))
}
我研究了一下http.Handle
和http.HandleFunc
之间的区别,并天真地认为我可以将http.HandleFunc
替换为http.Handle
。但是这种方法是错误的,因为HandlerFunc
中包含的逻辑永远不会执行:
var limiter = rate.NewLimiter(rate.Every(1*time.Hour/3), 1)
func limit(next http.HandlerFunc) http.HandlerFunc {
return func(res http.ResponseWriter, req *http.Request) {
if limiter.Allow() == false {
http.Error(res, http.StatusText(429), http.StatusTooManyRequests)
return
}
next.ServeHTTP(res, req)
}
}
func main() {
//mux := http.NewServeMux()
http.HandleFunc("/", createNewToken)
http.HandleFunc("/notify", sendPushNotificationToAllTokens)
// 尝试只对/notify端点进行速率限制
log.Fatal(http.ListenAndServeTLS(":5050", "localhost.crt", "localhost.key", limit(sendPushNotificationToAllTokens)))
有人能解释一下为什么这样做不起作用,以及我如何解决这个问题,只对特定的端点进行速率限制吗?
英文:
I am new to GoLang and working on my first API. I have two endpoints, and I want to rate limit only one of them. I found a helpful tutorial to get me started, and I've based my approach off of the tutorial, recognizing that this approach will rate limit both of my endpoints:
var limiter = rate.NewLimiter(rate.Every((1*time.Hour)/3), 1)
func limit(next http.Handler) http.Handler {
return http.HandlerFunc(func(res http.ResponseWriter, req *http.Request) {
if limiter.Allow() == false {
http.Error(res, http.StatusText(429), http.StatusTooManyRequests)
return
}
next.ServeHTTP(res, req)
})
}
func main() {
mux := http.NewServeMux()
mux.HandleFunc("/", createNewToken)
mux.HandleFunc("/notify", sendPushNotificationToAllTokens)
log.Fatal(http.ListenAndServeTLS(":5050", "localhost.crt", "localhost.key", limit(mux)))
}
I researched the difference between http.Handle and http.HandleFunc and naively believed that I could substitute http.HandleFunc
for http.Handle
. This approach is completely flawed as the logic contained in the HandlerFunc
never executes:
var limiter = rate.NewLimiter(rate.Every(1*time.Hour/3), 1)
func limit(next http.HandlerFunc) http.HandlerFunc {
return func(res http.ResponseWriter, req *http.Request) {
if limiter.Allow() == false {
http.Error(res, http.StatusText(429), http.StatusTooManyRequests)
return
}
next.ServeHTTP(res, req)
}
}
func main() {
//mux := http.NewServeMux()
http.HandleFunc("/", createNewToken)
http.HandleFunc("/notify", sendPushNotificationToAllTokens)
// attempt to only rate limit the /notify endpoint
log.Fatal(http.ListenAndServeTLS(":5050", "localhost.crt", "localhost.key", limit(sendPushNotificationToAllTokens)))
Can anyone explain why this does not work, and how I could approach this problem to only rate limit a specific endpoint?
答案1
得分: 2
在这里,使用普通的http.Handler
和http.HandlerFunc
之间的区别并不重要。http.HandleFunc
只是一种将常规函数转换为http.Handler
的方法 - 它本质上与您原始版本的limit
函数做的事情是一样的。
您的limit
函数的实现都看起来不错;可能第二个更好,因为它更简单。问题实际上出现在main
函数中。当您调用http.ListenAndServeTLS
并为最后一个参数提供一个值时,它要求只使用您作为最后一个参数传递的处理程序作为根请求处理程序。除非您将nil
作为最后一个参数传递进去,否则对http.Handle()
或http.HandleFunc()
的任何调用都将被忽略。
相反,您想要做的是将limit
应用于要限制的特定处理程序。您有两个选项。首先,您可以像在第一个代码片段中那样使用ServeMux
:
func main() {
mux := http.NewServeMux()
mux.HandleFunc("/", createNewToken)
// 仅限制“/notify”处理程序。
mux.HandleFunc("/notify", limit(sendPushNotificationToAllTokens))
// 不要限制整个mux。
log.Fatal(http.ListenAndServeTLS(":5050", "localhost.crt", "localhost.key", mux))
}
或者,您可以像在第二个代码片段中那样做,但是将http.ListenAndServeTLS
的最后一个参数传递为nil
,以便使用默认的http.ServeMux
,这意味着对http.HandleFunc()
的调用将被尊重:
func main() {
http.HandleFunc("/", createNewToken)
// 仅限制“/notify”处理程序。
http.HandleFunc("/notify", limit(sendPushNotificationToAllTokens))
// 在这里传递nil,以便使用http.DefaultServeMux。
log.Fatal(http.ListenAndServeTLS(":5050", "localhost.crt", "localhost.key", nil))
}
对于简单的应用程序,第一种方法可能是可以的。对于任何更复杂的情况,我建议使用后一种方法,因为它可以在打开多个服务器或执行其他更复杂的操作时正常工作。
英文:
The distinction between using a plain http.Handler
and a http.HanlderFunc
doesn't really matter here. http.HandleFunc
is just a way to convert a regular function into a http.Handler
- it essentially does the same thing as your original version of limit
.
Your implementations of limit
both look fine; probably the second is better because it's simpler. Instead, the issue is in main
. When you call http.ListenAndServeTLS
and provide a value for the final argument, it requests that only the handler you pass in as that final argument be used as the root request handler. Any calls to http.Handle()
or http.HandleFunc()
are ignored unless you pass in nil
as this final argument.
What you want to do instead is apply limit
to the specific handler you want to limit. You have two options for this. First, you can use a ServeMux
like in your first code snippet:
func main() {
mux := http.NewServeMux()
mux.HandleFunc("/", createNewToken)
// Limit only the handler for "/notify".
mux.HandleFunc("/notify", limit(sendPushNotificationToAllTokens))
// Don't limit the whole mux.
log.Fatal(http.ListenAndServeTLS(":5050", "localhost.crt", "localhost.key", mux))
}
Alternatively, you can do something more like your second code snippet, but pass in nil
for the final argument to http.ListenAndServeTLS
so that the default http.ServeMux
is used, meaning that the calls to http.HandleFunc()
will be respected:
func main() {
http.HandleFunc("/", createNewToken)
// Limit only the handler for "/notify".
http.HandleFunc("/notify", limit(sendPushNotificationToAllTokens))
// Pass in nil here so that http.DefaultServeMux is used.
log.Fatal(http.ListenAndServeTLS(":5050", "localhost.crt", "localhost.key", nil))
}
For a simple application, the first approach is probably fine. For anything more complex, I'd recommend the later approach because it will work if you open multiple servers or do other more complex things.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论