猿问

基于主机地址的限速http请求

我使用 Go 开发了一个 HTTP 服务器。现在我想实现一个速率限制器,这样我就可以检查来自特定 IP 的 HTTP 请求是否在 1 分钟内发送超过 10 个 HTTP 请求,我可以将该 IP 放入阻止列表中一段时间,比如(1 小时) 并且与此同时,同一订阅者在他/她处于阻塞期时正在发送请求,我将从 HTTP 服务器发送 429 错误响应。


我为此编写了一个代码,但在此能够阻止 IP 地址,但它会在 1 小时后解除对所有 IP 的阻止。我期待先到先解锁。


Package main


import (

    "log"

    "net/http"

    "strings"

    "time"

)


func main() {

    fs := http.FileServer(http.Dir("./html/"))

    http.Handle("/", fs)

    log.Println("Listening..")

    go clearLastRequestsIPs()

    go clearBlockedIPs()

    err := http.ListenAndServe(":8080", middleware(nil))

    if err != nil {

        log.Fatalln(err)

    }

}


// Stores last requests IPs

var lastRequestsIPs []string


// Block IP for 1 hours

var blockedIPs []string


func middleware(next http.Handler) http.Handler {

    return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {

        ipAddr := strings.Split(r.RemoteAddr, ":")[0]

        if existsBlockedIP(ipAddr) {

            http.Error(w, "", http.StatusTooManyRequests)

            return

        }

        // how many requests the current IP made in last 5 mins

        requestCounter := 0

        for _, ip := range lastRequestsIPs {

            if ip == ipAddr {

                requestCounter++

            }

        }

        if requestCounter >= 1000 {

            blockedIPs = append(blockedIPs, ipAddr)

            http.Error(w, "", http.StatusTooManyRequests)

            return

        }

        lastRequestsIPs = append(lastRequestsIPs, ipAddr)


        if next == nil {

            http.DefaultServeMux.ServeHTTP(w, r)

            return

        }

        next.ServeHTTP(w, r)

    })

}


func existsBlockedIP(ipAddr string) bool {

    for _, ip := range blockedIPs {

        if ip == ipAddr {

            return true

        }

    }

    return false

}


func existsLastRequest(ipAddr string) bool {

    for _, ip := range lastRequestsIPs {

        if ip == ipAddr {

            return true

        }

    }

    return false

}


aluckdog
浏览 108回答 2
2回答

HUWWW

你可以使用一些像这样的中间件:type Limiter struct {    ipCount map[string]int    sync.Mutex}var limiter Limiterfunc init() {    limiter.ipCount = make(map[string]int)}func limit(next http.Handler) http.Handler {  return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {    // Get the IP address for the current user.    ip, _, err := net.SplitHostPort(r.RemoteAddr)    if err != nil {      log.Println(err.Error())      http.Error(w, "Internal Server Error", http.StatusInternalServerError)      return    }    // Get the # of times the visitor has visited in the last 60 seconds    limiter.Lock()    count, ok := limiter.ipCount[ip]    if !ok {        limiter.ipCount[ip] = 0    }    if count > 10 {      limiter.Unlock()      http.Error(w, http.StatusText(429), http.StatusTooManyRequests)      return    } else {        limiter.ipCount[ip]++    }    time.AfterFunc(time.Second * 60, func() {        limiter.Lock()        limiter.ipCount[ip]--        limiter.Unlock()    })    if limiter.ipCount[ip] == 10 {        // set it to 20 so the decrement timers will only decrease it to        // 10, and they stay blocked until the next timer resets it to 0        limiter.ipCount[ip] = 20        time.AfterFunc(time.Hour, func() {            limiter.Lock()            limiter.ipCount[ip] = 0            limiter.Unlock()        })    }    limiter.Unlock()    next.ServeHTTP(w, r)  })}    

qq_花开花谢_0

rate.NewLimiter()从 golang.org/x/time/rate 包中使用。
随时随地看视频慕课网APP

相关分类

Go
我要回答