limit

package module
v0.0.0-...-8f56f07 Latest Latest
Warning

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

Go to latest
Published: Mar 4, 2019 License: MIT Imports: 9 Imported by: 0

README

limit

A sliding window rate-limited backed by Redis inspired by https://engagor.github.io/blog/2017/05/02/sliding-window-rate-limiter-redis/.

Installation

$ go get -u github.com/codingconcepts/limit

How it works

This library makes use of sorted sets in Redis for rate-limiting. A call to rate.Allowed takes the ID of the unique caller you're limiting (e.g. an IP address) which will become its own sorted set in Redis.

As an example, we'll assume a unique ID of "8.8.8.8" and a rate that allows for 10 calls per second. Every call to Allowed will perform the following in Redis:

  • Start a transaction.
  • Remove all items from the set that were performed over 1 second ago.
  • Return all of the items in the sorted set (this allows us to determine if the limit has been reached).
  • Add the current timestamp to the sorted set.
  • Add an expiry of 1 second to the sorted set.
  • Commit the transaction.

Usage

Basic
package main

import (
	"fmt"
	"time"

	"github.com/codingconcepts/limit"
	"github.com/go-redis/redis"
)

func main() {
	client := redis.NewClient(&redis.Options{
		Addr:     "localhost:6379",
		Password: "",
		DB:       0,
	})

	if err := client.Ping().Err(); err != nil {
		log.Fatalf("error pinging redis: %v", err)
	}

	// Create a limiter allowing 3 calls every second.
	rate := limit.New(client).Rate(3, time.Second)
	
	fmt.Println(rate.Allowed("8.8.8.8")) // true 3 <nil>
	fmt.Println(rate.Allowed("8.8.8.8")) // true 2 <nil>
	fmt.Println(rate.Allowed("8.8.8.8")) // true 1 <nil>
	fmt.Println(rate.Allowed("8.8.8.8")) // false 0 <nil>
}
HTTP
package main

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

	"github.com/codingconcepts/limit"
	"github.com/go-redis/redis"
)

func main() {
	client := redis.NewClient(&redis.Options{
		Addr:     "localhost:6379",
		Password: "",
		DB:       0,
	})

	if err := client.Ping().Err(); err != nil {
		log.Fatalf("error pinging redis: %v", err)
	}

	limiter := limit.New(client)

	http.Handle("/", limiter.LimitFuncHandler(3, time.Second*10, handler))
	http.ListenAndServe(":1234", nil)
}

func handler(w http.ResponseWriter, req *http.Request) {
	w.Write([]byte("hello"))
}

Todos

  • Need a fallback mechanism, in case Redis can't be reached.

Documentation

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func New

func New(c redis.Cmdable) *limiter

New returns an instance of the Redis wrapper. It has no ability to rate-limit by itself and requires a call to Rate, which provides a specific sliding window period or a call to one of the HTTP handler implementations which themselves create and use an instance of the sliding window period.

Types

type Error

type Error struct {
	Code          int     `json:"-"`
	MaxCalls      int     `json:"maxCalls"`
	WindowSeconds float64 `json:"windowSeconds"`
}

Error allows rate-specific information to be returned to the caller in the event that the limit has been reached.

func (Error) Error

func (e Error) Error() string

Error returns the JSON representation of the Error object or an empty string in the case of a marshalling error.

Directories

Path Synopsis
examples

Jump to

Keyboard shortcuts

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