【问题标题】:Rate limit with golang.org/x/time/rate api requestgolang.org/x/time/rate api 请求的速率限制
【发布时间】:2020-03-23 15:30:51
【问题描述】:

我已经为 1 天内的 api 登录限制 50 个请求创建了函数

变量限制 = 50

package middleware

import (
    "log"
    "net"
    "net/http"
    "sync"
    "time"

    "golang.org/x/time/rate"
)

// Create a custom request struct which holds the rate limiter for each
// visitor and the last time that the request was seen.
type request struct {
    limiter  *rate.Limiter
    lastSeen time.Time
}

// Change the the map to hold values of the type request.
// defaultTime using 3 minutes
var requests = make(map[string]*request)
var mu sync.Mutex

func getRequest(ip string, limit int) *rate.Limiter {
    mu.Lock()
    defer mu.Unlock()

    v, exists := requests[ip]
    if !exists {
        limiter := rate.NewLimiter(1, limit)
        requests[ip] = &request{limiter, time.Now()}
        return limiter
    }
    // Update the last seen time for the visitor.
    v.lastSeen = time.Now()
    return v.limiter
}

func throttle(next http.Handler, limit int) http.Handler {
    return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        ip, _, err := net.SplitHostPort(r.RemoteAddr)
        if err != nil {
            log.Println(err.Error())
            http.Error(w, "Internal Server Error", http.StatusInternalServerError)
            return
        }
        limiter := getRequest(ip, limit)
        fmt.Println(limiter.Allow())
        if limiter.Allow() == false {
            http.Error(w, http.StatusText(http.StatusTooManyRequests), http.StatusTooManyRequests)
            return
        }
        next.ServeHTTP(w, r)
    })
}

正确吗?

因为我尝试它仍然通过,功能限制不起作用

我怀疑 NewLimiter()

 limiter := rate.NewLimiter(1, limit)

这意味着 1 个用户只能请求登录 50 req /days 吗? (我已经读过文档但我不明白)

【问题讨论】:

    标签: go limit rate


    【解决方案1】:

    来自rate docs

    func NewLimiter(r Limit, b int) *Limiter
    

    NewLimiter 返回一个新的限制器,它允许事件达到速率 r 和 最多允许突发 b 个令牌。


    所以第一个参数是速率限制,而不是第二个。 Burst 是您希望允许比速率限制更快 发生的请求数量 - 通常使用值 1 来禁止突发,任何更高的值都会让这个数量的请求在常规速率限制开始生效。无论如何...

    要根据您的需要创建rate.Limit,您可以使用帮助函数rate.Every()

    rt := rate.Every(24*time.Hour / 50)
    
    limiter := rate.NewLimiter(rt, 1)
    

    【讨论】:

    • 我已经用 rt := rate.Every(5*time.Minute / 5) 限制器进行测试 := rate.NewLimiter(rt, 1) 表示 5req/5 分钟,所以如果我请求 6 次5分钟会给我错误(很多请求)??但是当我测试它不应该是
    • 您确定您的测试有效吗?您将每个限制器存储在 ip 键下 - 您确定该键每次都相同吗?在切换到单独的 ip 限制器之前,可能先移除单个限制器 - 并使用单个限制器 - 以证明限制器首先工作。
    • @agnyreza 这是一个给定可接受的请求率的速率限制器的工作示例;然后是超出的水平 - 然后切换回来:play.golang.org/p/dWH6ZQY8tD1
    • @colm.anseo 我稍微修改了你的代码,只是使用 0.0 作为 Limit.. 并且发生了一些奇怪的事情:所有对 Allow() 的调用都返回 true.. 使用 limit 0.0 不应该允许任何要求..对吗? play.golang.org/p/hVPT-KUPMAN
    • @colm.anseo 对不起这个:play.golang.org/p/_vqaCPq58SZ 因为第一个使用 Every(如果为 0,Every 返回 Inf)
    【解决方案2】:

    NewLimited(1, 50) 表示 1 个请求/秒,最多可爆发 50 个请求。它是一个令牌桶,这意味着有 50 个令牌,每个接受的 API 调用使用一个令牌,并且令牌以给定的速率重新生成,最多 burst。您的代码正在为每个 IP 地址创建一个限制器,因此这是每个 IP 地址的限制(我猜您是在近似为一个 IP 地址是一个用户)。

    如果您在单个持久服务器上运行,并且服务器和代码永远不会重新启动,那么您可以通过指定速率为 50 / (3600*24) 和突发 50 来获得每个用户每天 50 个请求。 (注:3600*24 是一天中的秒数)。但是您正在使用的速率限制包并不是为这种粗略的速率限制而设计的(按每天请求的顺序)——它旨在防止服务器在短期内因流量过大而过载(按每秒请求的顺序) )。

    您可能想要一个与数据库或类似工具一起使用的限速器(可能使用令牌桶方案,因为这样可以有效地实现)。可能在某个地方有一个包,但我不知道我的头顶有一个。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-08-07
      • 1970-01-01
      • 1970-01-01
      • 2018-11-05
      • 2013-12-13
      相关资源
      最近更新 更多