limiter

Dead simple rate limit middleware for Go.

  • Owner: ulule/limiter
  • Platform:
  • License:: MIT License
  • Category::
  • Topic:
  • Like:
    0
      Compare:

Github stars Tracking Chart

Limiter

Documentation
License
Build Status
Go Report Card

Dead simple rate limit middleware for Go.

  • Simple API
  • "Store" approach for backend
  • Redis support (but not tied too)
  • Middlewares: HTTP, FastHTTP and Gin

Installation

Using Go Modules

$ go get github.com/ulule/limiter/v3@v3.4.0

Dep backport:

Please use v3-dep branch.

Usage

In five steps:

  • Create a limiter.Rate instance (the number of requests per period)
  • Create a limiter.Store instance (see Redis or In-Memory)
  • Create a limiter.Limiter instance that takes store and rate instances as arguments
  • Create a middleware instance using the middleware of your choice
  • Give the limiter instance to your middleware initializer

Example:

// Create a rate with the given limit (number of requests) for the given
// period (a time.Duration of your choice).
import "github.com/ulule/limiter/v3"

rate := limiter.Rate{
    Period: 1 * time.Hour,
    Limit:  1000,
}

// You can also use the simplified format "<limit>-<period>"", with the given
// periods:
//
// * "S": second
// * "M": minute
// * "H": hour
// * "D": day
//
// Examples:
//
// * 5 reqs/second: "5-S"
// * 10 reqs/minute: "10-M"
// * 1000 reqs/hour: "1000-H"
// * 2000 reqs/day: "2000-D"
//
rate, err := limiter.NewRateFromFormatted("1000-H")
if err != nil {
    panic(err)
}

// Then, create a store. Here, we use the bundled Redis store. Any store
// compliant to limiter.Store interface will do the job. The defaults are
// "limiter" as Redis key prefix and a maximum of 3 retries for the key under
// race condition.
import "github.com/ulule/limiter/v3/drivers/store/redis"

store, err := redis.NewStore(client)
if err != nil {
    panic(err)
}

// Alternatively, you can pass options to the store with the "WithOptions"
// function. For example, for Redis store:
import "github.com/ulule/limiter/v3/drivers/store/redis"

store, err := redis.NewStoreWithOptions(pool, limiter.StoreOptions{
    Prefix:   "your_own_prefix",
    MaxRetry: 4,
})
if err != nil {
    panic(err)
}

// Or use a in-memory store with a goroutine which clears expired keys.
import "github.com/ulule/limiter/v3/drivers/store/memory"

store := memory.NewStore()

// Then, create the limiter instance which takes the store and the rate as arguments.
// Now, you can give this instance to any supported middleware.
instance := limiter.New(store, rate)

See middleware examples:

How it works

The ip address of the request is used as a key in the store.

If the key does not exist in the store we set a default
value with an expiration period.

You will find two stores:

  • Redis: rely on TTL and incrementing the rate limit on each request.
  • In-Memory: rely on a fork of go-cache with a goroutine to clear expired keys using a default interval.

When the limit is reached, a 429 HTTP status code is sent.

Why Yet Another Package

You could ask us: why yet another rate limit package?

Because existing packages did not suit our needs.

We tried a lot of alternatives:

  1. Throttled. This package uses the generic cell-rate algorithm. To cite the
    documentation: "The algorithm has been slightly modified from its usual form to
    support limiting with an additional quantity parameter, such as for limiting the
    number of bytes uploaded"
    . It is brillant in term of algorithm but
    documentation is quite unclear at the moment, we don't need burst feature for
    now, impossible to get a correct After-Retry (when limit exceeds, we can still
    make a few requests, because of the max burst) and it only supports http.Handler
    middleware (we use Gin). Currently, we only need to return 429
    and X-Ratelimit-* headers for n reqs/duration.

  2. Speedbump. Good package but maybe too lightweight. No Reset support,
    only one middleware for Gin framework and too Redis-coupled. We rather
    prefer to use a "store" approach.

  3. Tollbooth. Good one too but does both too much and too little. It limits by
    remote IP, path, methods, custom headers and basic auth usernames... but does not
    provide any Redis support (only in-memory) and a ready-to-go middleware that sets
    X-Ratelimit-* headers. tollbooth.LimitByRequest(limiter, r) only returns an HTTP
    code.

  4. ratelimit. Probably the closer to our needs but, once again, too
    lightweight, no middleware available and not active (last commit was in August
    2014). Some parts of code (Redis) comes from this project. It should deserve much
    more love.

There are other many packages on GitHub but most are either too lightweight, too
old (only support old Go versions) or unmaintained. So that's why we decided to
create yet another one.

Contributing

Don't hesitate ;)

Main metrics

Overview
Name With Ownerulule/limiter
Primary LanguageGo
Program languageMakefile (Language Count: 4)
Platform
License:MIT License
所有者活动
Created At2015-10-02 08:12:38
Pushed At2024-12-10 12:15:49
Last Commit At2024-10-14 17:15:13
Release Count27
Last Release Namev3.11.2 (Posted on )
First Release Namev1.0.0 (Posted on )
用户参与
Stargazers Count2.2k
Watchers Count27
Fork Count157
Commits Count429
Has Issues Enabled
Issues Count75
Issue Open Count9
Pull Requests Count126
Pull Requests Open Count5
Pull Requests Close Count53
项目设置
Has Wiki Enabled
Is Archived
Is Fork
Is Locked
Is Mirror
Is Private