限制特定端点的速率

huangapple go评论78阅读模式
英文:

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.Handlehttp.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.Handlerhttp.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.

huangapple
  • 本文由 发表于 2022年11月11日 11:13:39
  • 转载请务必保留本文链接:https://go.coder-hub.com/74397644.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定