ratelimiter

package module
v0.5.2 Latest Latest
Warning

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

Go to latest
Published: Mar 10, 2017 License: MIT Imports: 5 Imported by: 0

README

ratelimiter-go

The fastest abstract rate limiter, base on memory or redis storage.

Build Status Coverage Status License GoDoc

Features

  • Distributed
  • Atomicity
  • High-performance
  • Support redis cluster
  • Support memory storage

Installation

go get github.com/teambition/ratelimiter-go

HTTP Server Demo

Try into github.com/teambition/ratelimiter-go directory:

go run example/main.go

Visit: http://127.0.0.1:8080/

package main

import (
	"fmt"
	"html"
	"log"
	"net/http"
	"strconv"
	"time"

	ratelimiter "github.com/teambition/ratelimiter-go"
	redis "gopkg.in/redis.v5"
)

// Implements RedisClient for redis.Client
type redisClient struct {
	*redis.Client
}

func (c *redisClient) RateDel(key string) error {
	return c.Del(key).Err()
}
func (c *redisClient) RateEvalSha(sha1 string, keys []string, args ...interface{}) (interface{}, error) {
	return c.EvalSha(sha1, keys, args...).Result()
}
func (c *redisClient) RateScriptLoad(script string) (string, error) {
	return c.ScriptLoad(script).Result()
}

func main() {
	// use memory
	// limiter := ratelimiter.New(ratelimiter.Options{
	// 	Max:      10,
	// 	Duration: time.Minute, // limit to 1000 requests in 1 minute.
	// })

	// or use redis
	client := redis.NewClient(&redis.Options{
		Addr: "localhost:6379",
	})
	limiter := ratelimiter.New(ratelimiter.Options{
		Max:      10,
		Duration: time.Minute, // limit to 1000 requests in 1 minute.
		Client:   &redisClient{client},
	})

	http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
		res, err := limiter.Get(r.URL.Path)
		if err != nil {
			http.Error(w, err.Error(), 500)
			return
		}

		header := w.Header()
		header.Set("X-Ratelimit-Limit", strconv.FormatInt(int64(res.Total), 10))
		header.Set("X-Ratelimit-Remaining", strconv.FormatInt(int64(res.Remaining), 10))
		header.Set("X-Ratelimit-Reset", strconv.FormatInt(res.Reset.Unix(), 10))

		if res.Remaining >= 0 {
			w.WriteHeader(200)
			fmt.Fprintf(w, "Path: %q\n", html.EscapeString(r.URL.Path))
			fmt.Fprintf(w, "Remaining: %d\n", res.Remaining)
			fmt.Fprintf(w, "Total: %d\n", res.Total)
			fmt.Fprintf(w, "Duration: %v\n", res.Duration)
			fmt.Fprintf(w, "Reset: %v\n", res.Reset)
		} else {
			after := int64(res.Reset.Sub(time.Now())) / 1e9
			header.Set("Retry-After", strconv.FormatInt(after, 10))
			w.WriteHeader(429)
			fmt.Fprintf(w, "Rate limit exceeded, retry in %d seconds.\n", after)
		}
	})
	log.Fatal(http.ListenAndServe(":8080", nil))
}

Node.js version: thunk-ratelimiter

Documentation

The docs can be found at godoc.org, as usual.

License

ratelimiter-go is licensed under the MIT license. Copyright © 2016 Teambition.

Documentation

Overview

Package ratelimiter provides the fastest abstract rate limiter, base on redis.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type Limiter

type Limiter struct {
	// contains filtered or unexported fields
}

Limiter struct.

func New

func New(opts Options) *Limiter

New returns a Limiter instance with given options. If options.Client omit, the limiter is a memory limiter

func (*Limiter) Get

func (l *Limiter) Get(id string, policy ...int) (Result, error)

Get get a limiter result for id. support custom limiter policy.

Get get a limiter result:

userID := "user-123456"
res, err := limiter.Get(userID)
if err == nil {
    fmt.Println(res.Reset)     // 2016-10-11 21:17:53.362 +0800 CST
    fmt.Println(res.Total)     // 100
    fmt.Println(res.Remaining) // 100
    fmt.Println(res.Duration)  // 1m
}

Get get a limiter result with custom limiter policy:

id := "id-123456"
policy := []int{100, 60000, 50, 60000, 50, 120000}
res, err := limiter.Get(id, policy...)

func (*Limiter) Remove

func (l *Limiter) Remove(id string) error

Remove remove limiter record for id

type Options

type Options struct {
	Max      int           // The max count in duration for no policy, default is 100.
	Duration time.Duration // Count duration for no policy, default is 1 Minute.
	Prefix   string        // Redis key prefix, default is "LIMIT:".
	Client   RedisClient   // Use a redis client for limiter, if omit, it will use a memory limiter.
}

Options for Limiter

type RedisClient added in v0.3.0

type RedisClient interface {
	RateDel(string) error
	RateEvalSha(string, []string, ...interface{}) (interface{}, error)
	RateScriptLoad(string) (string, error)
}

RedisClient defines a redis client struct that ratelimiter need. Examples: https://github.com/teambition/ratelimiter-go/blob/master/ratelimiter_test.go#L18

Implements RedisClient for a simple redis client:

import "gopkg.in/redis.v4"

type redisClient struct {
    *redis.Client
}

func (c *redisClient) RateDel(key string) error {
    return c.Del(key).Err()
}
func (c *redisClient) RateEvalSha(sha1 string, keys []string, args ...interface{}) (interface{}, error) {
    return c.EvalSha(sha1, keys, args...).Result()
}
func (c *redisClient) RateScriptLoad(script string) (string, error) {
    return c.ScriptLoad(lua).Result()
}

Implements RedisClient for a cluster redis client:

import "gopkg.in/redis.v4"

type clusterClient struct {
    *redis.ClusterClient
}

func (c *clusterClient) RateDel(key string) error {
    return c.Del(key).Err()
}
func (c *clusterClient) RateEvalSha(sha1 string, keys []string, args ...interface{}) (interface{}, error) {
    return c.EvalSha(sha1, keys, args...).Result()
}
func (c *clusterClient) RateScriptLoad(script string) (string, error) {
    var sha1 string
    err := c.ForEachMaster(func(client *redis.Client) error {
        res, err := client.ScriptLoad(script).Result()
        if err == nil {
            sha1 = res
        }
        return err
    })
    return sha1, err
}

Uses it:

client := redis.NewClient(&redis.Options{
    Addr: "localhost:6379",
})
limiter := ratelimiter.New(ratelimiter.Options{Client: redisClient{client}})

type Result

type Result struct {
	Total     int           // It Equals Options.Max, or policy max
	Remaining int           // It will always >= -1
	Duration  time.Duration // It Equals Options.Duration, or policy duration
	Reset     time.Time     // The limit record reset time
}

Result of limiter.Get

Directories

Path Synopsis

Jump to

Keyboard shortcuts

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