limiter

package module
v0.0.0-...-b745066 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Oct 23, 2015 License: MIT Imports: 10 Imported by: 0

README

Limiter

Build Status

Dead simple rate limit middleware for Go.

  • Simple API
  • "Store" approach for backend
  • Redis support (but not tied too)
  • Middlewares: HTTP and go-json-rest

Installation

$ go get github.com/ulule/limiter

Usage

In five steps:

  • Create a limiter.Rate instance (the number of requests per period)
  • Create a limiter.Store instance (see store_redis for Redis or store_memory for 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).
rate := limiter.Rate{
    Period: 1 * time.Hour,
    Limit:  int64(1000),
}

// You can also use the simplified format "<limit>-<period>"", with the given
// periods:
//
// * "S": second
// * "M": minute
// * "H": hour
//
// Examples:
//
// * 5 reqs/second: "5-S"
// * 10 reqs/minute: "10-M"
// * 1000 reqs/hour: "1000-H"
//
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.
store, err := limiter.NewRedisStore(pool)
if err != nil {
    panic(err)
}

// Alternatively, you can pass options to the store with the "WithOptions"
// function. For example, for Redis store:
store, err := limiter.NewRedisStoreWithOptions(pool, limiter.StoreOptions{
    Prefix:   "your_own_prefix",
    MaxRetry: 4,
})

if err != nil {
    panic(err)
}

// Or use a in-memory store with a goroutine which clear expired keys every 30 seconds
store := limiter.NewMemoryStore("prefix_for_keys", 30*time.Second)

// Then, create the limiter instance which takes the store and the rate as arguments.
// Now, you can give this instance to any supported middleware.
limiterInstance := limiter.NewLimiter(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:

  • RedisStore: rely on TTL and incrementing the rate limit on each request
  • MemoryStore: rely on go-cache with a goroutine to clear expired keys using a default interval

When the limit is reached, a 429 HTTP 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 go-json-rest). 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 less. 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 ;)

Documentation

Index

Constants

View Source
const (
	// DefaultPrefix is the default prefix to use for the key in the store.
	DefaultPrefix = "limiter"

	// DefaultMaxRetry is the default maximum number of key retries under
	// race condition (mainly used with database-based stores).
	DefaultMaxRetry = 3

	// DefaultCleanUpInterval is the default time duration for cleanup.
	DefaultCleanUpInterval = 30 * time.Second
)

Variables

This section is empty.

Functions

func GetIP

func GetIP(r *http.Request) net.IP

GetIP returns IP address from request.

func GetIPKey

func GetIPKey(r *http.Request) string

GetIPKey extracts IP from request and returns hashed IP to use as store key.

func Random

func Random(min, max int) int

Random return a random integer between min and max.

Types

type Context

type Context struct {
	Limit     int64
	Remaining int64
	Reset     int64
	Reached   bool
}

Context is the limit context.

type GJRMiddleware

type GJRMiddleware struct {
	Limiter *Limiter
}

GJRMiddleware is the go-json-rest middleware.

func NewGJRMiddleware

func NewGJRMiddleware(limiter *Limiter) *GJRMiddleware

NewGJRMiddleware returns a new instance of go-json-rest middleware.

func (*GJRMiddleware) MiddlewareFunc

func (m *GJRMiddleware) MiddlewareFunc(h rest.HandlerFunc) rest.HandlerFunc

MiddlewareFunc is the middleware method (handler).

type HTTPMiddleware

type HTTPMiddleware struct {
	Limiter *Limiter
}

HTTPMiddleware is the basic HTTP middleware.

func NewHTTPMiddleware

func NewHTTPMiddleware(limiter *Limiter) *HTTPMiddleware

NewHTTPMiddleware return a new instance of go-json-rest middleware.

func (*HTTPMiddleware) Handler

func (m *HTTPMiddleware) Handler(h http.Handler) http.Handler

Handler the middleware handler.

type Limiter

type Limiter struct {
	Store Store
	Rate  Rate
}

Limiter is the limiter instance.

func NewLimiter

func NewLimiter(store Store, rate Rate) *Limiter

NewLimiter returns an instance of Limiter.

func (*Limiter) Get

func (l *Limiter) Get(key string) (Context, error)

Get returns the limit for the identifier.

type MemoryStore

type MemoryStore struct {
	Cache  *cache.Cache
	Prefix string
}

MemoryStore is the in-memory store.

func (*MemoryStore) Get

func (s *MemoryStore) Get(key string, rate Rate) (Context, error)

Get implement Store.Get() method.

type Rate

type Rate struct {
	Formatted string
	Period    time.Duration
	Limit     int64
}

Rate is the rate.

func NewRateFromFormatted

func NewRateFromFormatted(formatted string) (Rate, error)

NewRateFromFormatted returns the rate from the formatted version.

type RedisStore

type RedisStore struct {
	// The prefix to use for the key.
	Prefix string

	// github.com/garyburd/redigo Pool instance.
	Pool *redis.Pool

	// The maximum number of retry under race conditions.
	MaxRetry int
}

RedisStore is the redis store.

func (RedisStore) Get

func (s RedisStore) Get(key string, rate Rate) (Context, error)

Get returns the limit for the identifier.

type RedisStoreFunc

type RedisStoreFunc func(c redis.Conn, key string, rate Rate) ([]int, error)

RedisStoreFunc is a redis store function.

type Store

type Store interface {
	Get(key string, rate Rate) (Context, error)
}

Store is the common interface for limiter stores.

func NewMemoryStore

func NewMemoryStore() Store

NewMemoryStore creates a new instance of memory store with defaults.

func NewMemoryStoreWithOptions

func NewMemoryStoreWithOptions(options StoreOptions) Store

NewMemoryStoreWithOptions creates a new instance of memory store with options.

func NewRedisStore

func NewRedisStore(pool *redis.Pool) (Store, error)

NewRedisStore returns an instance of redis store.

func NewRedisStoreWithOptions

func NewRedisStoreWithOptions(pool *redis.Pool, options StoreOptions) (Store, error)

NewRedisStoreWithOptions returns an instance of redis store with custom options.

type StoreOptions

type StoreOptions struct {
	// Prefix is the prefix to use for the key.
	Prefix string

	// MaxRetry is the maximum number of retry under race conditions.
	MaxRetry int

	// CleanUpInterval is the interval for cleanup.
	CleanUpInterval time.Duration
}

StoreOptions are options for store.

Directories

Path Synopsis
examples
gjr

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL