rerate

package module
v3.0.1+incompatible Latest Latest
Warning

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

Go to latest
Published: Nov 18, 2018 License: MIT Imports: 5 Imported by: 0

README

rerate

Build Status GoDoc Go Report Card Coverage Status

rerate is a redis-based ratecounter and ratelimiter

  • Dead simple api
  • With redis as backend, multiple rate counters/limiters can work as a cluster
  • Count/Limit requests any period, 2 day, 1 hour, 5 minute or 2 second, it's up to you
  • Recording requests as a histotram, which can be used to visualize or monitor
  • Limit requests from single ip, userid, applicationid, or any other unique identifier

Tutorial

package main

import (
    "github.com/abo/rerate"
)

...

func main() {
    // redigo buckets
    pool := newRedisPool("localhost:6379", "")
    buckets := rerate.NewRedigoBuckets(pool)

    // OR redis buckets
    // client := redis.NewClient(&redis.Options{
	//	 Addr:     "localhost:6379",
	// 	 Password: "",
	// 	 DB:       0,
	// })
    // buckets := rerate.NewRedisBuckets(client)
    
    // Counter
    counter := rerate.NewCounter(buckets, "rl:test", 10 * time.Minute, 15 * time.Second)
    counter.Inc("click")
    c, err := counter.Count("click")
    
    // Limiter
    limiter := rerate.NewLimiter(buckets, "rl:test", 1 * time.Hour, 15 * time.Minute, 100)
    limiter.Inc("114.255.86.200")
    rem, err := limiter.Remaining("114.255.86.200")
    exceed, err := limiter.Exceeded("114.255.86.200")
}

Installation

Install rerate using the "go get" command:

go get github.com/abo/rerate

Documentation

Sample - Sparkline

    cd cmd/sparkline
    npm install webpack -g
    npm install
    webpack && go run main.go

Open http://localhost:8080 in Browser, And then move mouse.

Contributing

WELCOME

License

rerate is available under the The MIT License (MIT).

Documentation

Index

Examples

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type Buckets

type Buckets interface {
	Inc(key string, id int64) error
	Del(key string, ids ...int64) error
	Get(key string, ids ...int64) ([]int64, error)
}

Buckets a set of bucket, each bucket compute and return the number of occurs in itself

type BucketsFactory

type BucketsFactory func(size int64, ttl time.Duration) Buckets

BucketsFactory a interface to create Buckets

func NewRedigoBuckets

func NewRedigoBuckets(redis *redis.Pool) BucketsFactory

NewRedigoBuckets is a RedigoBuckets factory

func NewRedisV5Buckets

func NewRedisV5Buckets(redis *redis.Client) BucketsFactory

NewRedisV5Buckets is a RedisV5Buckets factory

type Counter

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

Counter count total occurs during a period, it will store occurs during every time slice interval: (now ~ now - interval), (now - interval ~ now - 2*interval)...

Example
package main

import (
	"fmt"
	"time"

	"github.com/abo/rerate"
	"github.com/gomodule/redigo/redis"
)

func newRedigoPool(server, password string) *redis.Pool {
	return &redis.Pool{
		MaxIdle:     3,
		IdleTimeout: 240 * time.Second,
		Dial: func() (redis.Conn, error) {
			c, err := redis.Dial("tcp", server)
			if err != nil {
				return nil, err
			}
			if len(password) == 0 {
				return c, err
			}

			if _, err := c.Do("AUTH", password); err != nil {
				c.Close()
				return nil, err
			}
			return c, err
		},
		TestOnBorrow: func(c redis.Conn, t time.Time) error {
			_, err := c.Do("PING")
			return err
		},
	}
}

func main() {
	redigoBuckets := rerate.NewRedigoBuckets(newRedigoPool("localhost:6379", ""))

	key := "pv-home"
	// pv count in 5s, try to release per 0.5s
	counter := rerate.NewCounter(redigoBuckets, "rr:test:count", 5*time.Second, 500*time.Millisecond)
	counter.Reset(key)

	ticker := time.NewTicker(1000 * time.Millisecond)
	go func() {
		for range ticker.C {
			counter.Inc(key)
		}
	}()

	time.Sleep(4500 * time.Millisecond)
	ticker.Stop()
	total, _ := counter.Count(key)
	his, _ := counter.Histogram(key)
	fmt.Println("total:", total, ", histogram:", his)
}
Output:

total: 4 , histogram: [0 1 0 1 0 1 0 1 0 0]

func NewCounter

func NewCounter(newBuckets BucketsFactory, prefix string, period, interval time.Duration) *Counter

NewCounter create a new Counter

func (*Counter) Count

func (c *Counter) Count(id string) (int64, error)

Count return total occurs in recent period

func (*Counter) Histogram

func (c *Counter) Histogram(id string) ([]int64, error)

Histogram return count histogram in recent period, order by time desc

func (*Counter) Inc

func (c *Counter) Inc(id string) error

Inc increment id's occurs with current timestamp, the count before period will be cleanup

func (*Counter) Reset

func (c *Counter) Reset(id string) error

Reset cleanup occurs, set it to zero

type Limiter

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

Limiter a redis-based ratelimiter

Example
package main

import (
	"fmt"
	"time"

	"github.com/abo/rerate"
	"github.com/gomodule/redigo/redis"
)

func newRedigoPool(server, password string) *redis.Pool {
	return &redis.Pool{
		MaxIdle:     3,
		IdleTimeout: 240 * time.Second,
		Dial: func() (redis.Conn, error) {
			c, err := redis.Dial("tcp", server)
			if err != nil {
				return nil, err
			}
			if len(password) == 0 {
				return c, err
			}

			if _, err := c.Do("AUTH", password); err != nil {
				c.Close()
				return nil, err
			}
			return c, err
		},
		TestOnBorrow: func(c redis.Conn, t time.Time) error {
			_, err := c.Do("PING")
			return err
		},
	}
}

func main() {
	redigoBuckets := rerate.NewRedigoBuckets(newRedigoPool("localhost:6379", ""))

	key := "pv-dashboard"
	// rate limit to 10/2s, release interval 0.2s
	limiter := rerate.NewLimiter(redigoBuckets, "rr:test:limit", 2*time.Second, 200*time.Millisecond, 10)
	limiter.Reset(key)

	ticker := time.NewTicker(200 * time.Millisecond)

	go func() {
		for range ticker.C {
			limiter.Inc(key)
			if exceed, _ := limiter.Exceeded(key); exceed {
				ticker.Stop()
			}
		}
	}()

	time.Sleep(20 * time.Millisecond)
	for i := 0; i < 20; i++ {
		time.Sleep(200 * time.Millisecond)

		if exceed, _ := limiter.Exceeded(key); exceed {
			fmt.Println("exceeded")
		} else {
			rem, _ := limiter.Remaining(key)
			fmt.Println("remaining", rem)
		}
	}
}
Output:

func NewLimiter

func NewLimiter(newBuckets BucketsFactory, pfx string, period, interval time.Duration, max int64) *Limiter

NewLimiter create a new redis-based ratelimiter the Limiter limits the rate to max times per period

func (*Limiter) Exceeded

func (l *Limiter) Exceeded(id string) (bool, error)

Exceeded is exceeded the rate limit or not

func (*Limiter) Remaining

func (l *Limiter) Remaining(id string) (int64, error)

Remaining return the number of requests left for the time window

type RedigoBuckets

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

RedigoBuckets is a Buckets using redigo as backend

func (*RedigoBuckets) Del

func (bs *RedigoBuckets) Del(key string, ids ...int64) error

Del delete bucket key:ids, or delete Buckets key when ids is empty.

func (*RedigoBuckets) Get

func (bs *RedigoBuckets) Get(key string, ids ...int64) ([]int64, error)

Get return bucket key:ids' occurs

func (*RedigoBuckets) Inc

func (bs *RedigoBuckets) Inc(key string, id int64) error

Inc increment bucket key:id 's occurs

type RedisV5Buckets

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

RedisV5Buckets is a Buckets using redis.v5 as backend

func (*RedisV5Buckets) Del

func (bs *RedisV5Buckets) Del(key string, ids ...int64) error

Del delete bucket key:ids, or delete Buckets key when ids is empty.

func (*RedisV5Buckets) Get

func (bs *RedisV5Buckets) Get(key string, ids ...int64) ([]int64, error)

Get return bucket key:ids' occurs

func (*RedisV5Buckets) Inc

func (bs *RedisV5Buckets) Inc(key string, id int64) error

Inc increment bucket key:id 's occurs

Directories

Path Synopsis
cmd

Jump to

Keyboard shortcuts

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