Я новичок в GoLang и работаю над своим первым 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)
// attempt to only rate limit the /notify endpoint
log.Fatal(http.ListenAndServeTLS(":5050", "localhost.crt", "localhost.key", limit(sendPushNotificationToAllTokens)))
Может ли кто-нибудь объяснить, почему это не работает, и как я могу решить эту проблему, чтобы ограничить скорость только для определенной конечной точки?
Различие между использованием простого http.Handler
и http.HanlderFunc
здесь не имеет большого значения. http.HandleFunc
— это просто способ преобразовать обычную функцию в http.Handler
— по сути, она делает то же самое, что и исходная версия limit
.
Обе ваши реализации limit
выглядят нормально; наверное второй лучше, потому что он проще. Вместо этого проблема в main
. Когда вы вызываете http.ListenAndServeTLS
и указываете значение для последнего аргумента, он запрашивает, чтобы в качестве обработчика корневого запроса использовался только тот обработчик, который вы передаете в качестве этого последнего аргумента. Любые вызовы http.Handle()
или http.HandleFunc()
игнорируются, если вы не передадите nil
в качестве последнего аргумента.
Вместо этого вы хотите применить 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))
}
Для простого приложения, вероятно, подойдет первый подход. Для чего-то более сложного я бы рекомендовал более поздний подход, потому что он будет работать, если вы откроете несколько серверов или выполните другие более сложные действия.