我是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.HandleFunchttp.Handle之间的区别,天真地认为我可以用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应用于您想要限制的特定处理程序.为此,您有两个 Select .首先,您可以在第一个代码片段中使用类似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相关问答推荐

区分Terminal和Hook Zerolog Go中的错误级别日志(log)输出

gorm如何声明未自动更新的unix时间戳米尔斯字段

消费者在NAT中是如何实现的

如何存储来自异步Goroutine的返回值列表?

CGO Linux到Windows交叉编译中的未知类型名称

mockgen不创建模拟

为什么 `go mod` 占用了另一个磁盘上的空间而不是我的 GOPATH?

如何找到一个空闲的TPM句柄来保存新的密钥对对象?

如何将字节文件高效地读入int64切片?

用于提取 <*n 的正则表达式(其中 n 是一个数字)

Golang - POST 失败(NoSurf CSRF)

emersion/go-imap - imap.FetchRFC822:无效内存地址或零指针取消引用

Wire google Inject with multi return from provider 函数

如何将多个切片打印为一个切片?

如何使用 Status 字段创建 Kubernetes 对象?

go 是否对 struct 使用空间填充之类的东西?

golang 如何从字符串中查找表情符号?

递归数据 struct 解组在 Go Lang Protobuf 中给出错误无法解析无效的线格式数据

如何使用通用字段初始化匿名struct数组

Golang 中的无实体函数