go - 速率限制特定端点

标签 go rate-limiting go-http

我是 GoLang 新手,正在开发我的第一个 API。我有两个端点,我只想对其中之一进行速率限制。我找到了helpful tutorial为了让我开始,我的方法基于教程,认识到这种方法会限制我的两个端点的速率:

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 and 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)

    // attempt to only rate limit the /notify endpoint 
    log.Fatal(http.ListenAndServeTLS(":5050", "localhost.crt", "localhost.key", limit(sendPushNotificationToAllTokens)))

任何人都可以解释为什么这不起作用,以及我如何解决这个问题以仅对特定端点进行速率限制?

最佳答案

使用普通的http.Handlerhttp.HanlderFunc之间的区别在这里并不重要。 http.HandleFunc 只是将常规函数转换为 http.Handler 的一种方法 - 它本质上与原始版本的 limit 执行相同的操作>.

您的 limit 实现看起来都很好;可能第二个更好,因为它更简单。相反,问题出在 main 中。当您调用 http.ListenAndServeTLS 并为最终参数提供值时,它会请求仅将您作为最终参数传入的处理程序用作根请求处理程序。除非您传入 nil 作为最后一个参数,否则对 http.Handle()http.HandleFunc() 的任何调用都会被忽略。

您想要做的是将 limit 应用于您想要限制的特定处理程序。为此,您有两种选择。首先,您可以像第一个代码片段中那样使用 ServeMux:

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))
}

或者,您可以执行类似于第二个代码片段的操作,但将 nil 作为最后一个参数传递给 http.ListenAndServeTLS,以便默认的 http使用.ServeMux,这意味着对http.HandleFunc()的调用将受到尊重:

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))
}

对于简单的应用程序,第一种方法可能就很好。对于更复杂的事情,我建议使用后面的方法,因为如果您打开多个服务器或执行其他更复杂的事情,它会起作用。

关于go - 速率限制特定端点,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/74397644/

相关文章:

scala - 在实践中消息传递并发语言如何优于共享内存并发语言

go - `size *= b - a` 是什么意思?

queue - 分布式限速

go - 使用 `didip/tollbooth` 限制每小时最大请求数

list - map slice 的 map

go - 为什么我的 golang 程序创建了这么多线程?

Ruby - 用于速率限制的访问响应 header (Help Scout)

http - 从 ReadCloser 发送 HTTP Put 主体永远不会结束

go - 什么时候在 golang 中使用劫持?

go - 如何在Go中使用Cookie jar 保持相同的 session