agny reza
agny reza

Reputation: 187

Rate limit with golang.org/x/time/rate api request

I already created a function for limiting to 50 requests for API logins in one day.

var limit = 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)
    })
}

Is it correct?

Because when I try it, it still passes. The function limit is not working.

I doubt with NewLimiter()

 limiter := rate.NewLimiter(1, limit)

Does it mean one user only can request login 50 requests per day? (I already read the docs, but I do not understand.)

Upvotes: 4

Views: 10425

Answers (2)

Paul Hankin
Paul Hankin

Reputation: 58221

NewLimited(1, 50) means 1 request/second with a burst of up to 50 requests. It's a token bucket, which means that there are 50 tokens, each accepted API call uses up one token, and the tokens are regenerated at the given rate, up to burst. Your code is creating a limiter per IP address, so that's a limit per IP address (which I guess you are approximating as one IP address is one user).

If you're running on a single persistent server, and the server and code never restarts, then you may be able to get something like 50 requests/day per user by specifying a rate of 50 / (3600*24) and a burst of 50. (Note: 3600*24 is the number of seconds in a day). But the rate limiting package you're using is not designed for such coarse rate-limiting (on the order of requests per day) -- it's designed to prevent server overload under heavy traffic in the short term (on the order of requests per second).

You probably want a rate-limiter that works with a database or similar (perhaps using a token bucket scheme, since that can be implemented efficiently). Probably there's a package somewhere for that, but I don't know of one of the top of my head.

Upvotes: 1

colm.anseo
colm.anseo

Reputation: 22037

From the rate docs:

func NewLimiter(r Limit, b int) *Limiter

NewLimiter returns a new Limiter that allows events up to rate r and permits bursts of at most b tokens.


So the first parameter is the rate-limit, not the second. Burst is the number of requests you want to allow that occur faster than the rate-limit - typically one uses a value of 1 to disallow bursting, anything higher will let this number of requests in before the regular rate-limit kicks in. Anyway...

To create the rate.Limit for your needs, you can use the helper function rate.Every():

rt := rate.Every(24*time.Hour / 50)

limiter := rate.NewLimiter(rt, 1)

Upvotes: 3

Related Questions