ratelimiter

package module
v1.1.0 Latest Latest
Warning

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

Go to latest
Published: Apr 10, 2022 License: MIT Imports: 9 Imported by: 0

README

ratelimiter

Build Status go report card

中文 README

Simple version implementation of token bucket request frequency limiting.

ratelimiter library that supports in-memory and distributed eventually consistent redis stores (includes Gin middleware)

Go PKG Installation

go get -u github.com/axiaoxin-com/ratelimiter

Gin Middleware Example

GinMemRatelimiter

package main

import (
	"time"

	"gitlab.com/repotea-workspace/goratelimiter"
	"github.com/gin-gonic/gin"
)

func main() {
	r := gin.New()
	// Put a token into the token bucket every 1s
	// Maximum 1 request allowed per second
	r.Use(ratelimiter.GinMemRatelimiter(ratelimiter.GinRatelimiterConfig{
		// config: how to generate a limit key
		LimitKey: func(c *gin.Context) string {
			return c.ClientIP()
		},
		// config: how to respond when limiting
		LimitedHandler: func(c *gin.Context) {
			c.JSON(200, "too many requests!!!")
			c.Abort()
			return
		},
		// config: return ratelimiter token fill interval and bucket size
		TokenBucketConfig: func(*gin.Context) (time.Duration, int) {
			return time.Second * 1, 1
		},
	}))
	r.GET("/", func(c *gin.Context) {
		c.JSON(200, "hi")
	})
	r.Run()
}

GinRedisRatelimiter

package main

import (
	"time"

	"gitlab.com/repotea-workspace/gowkit"
	"gitlab.com/repotea-workspace/goratelimiter"
	"github.com/gin-gonic/gin"
	"github.com/go-redis/redis/v8"
)

func main() {
	r := gin.New()
	// Put a token into the token bucket every 1s
	// Maximum 1 request allowed per second
	rdb, err := gowkit.NewRedisClient(&redis.Options{})
	if err != nil {
		panic(err)
	}
	r.Use(ratelimiter.GinRedisRatelimiter(rdb, ratelimiter.GinRatelimiterConfig{
		// config: how to generate a limit key
		LimitKey: func(c *gin.Context) string {
			return c.ClientIP()
		},
		// config: how to respond when limiting
		LimitedHandler: func(c *gin.Context) {
			c.JSON(200, "too many requests!!!")
			c.Abort()
			return
		},
		// config: return ratelimiter token fill interval and bucket size
		TokenBucketConfig: func(*gin.Context) (time.Duration, int) {
			return time.Second * 1, 1
		},
	}))
	r.GET("/", func(c *gin.Context) {
		c.JSON(200, "hi")
	})
	r.Run()
}

Ratelimiter can be directly used in golang program. Examples:

MemRatelimiter

package main

import (
	"context"
	"fmt"
	"time"

	"gitlab.com/repotea-workspace/goratelimiter"
)

func main() {
	limiter := ratelimiter.NewMemRatelimiter()
	limitKey := "uniq_limit_key"
	tokenFillInterval := time.Second * 1
	bucketSize := 1
	for i := 0; i < 3; i++ {
		// 1st and 3nd is allowed
		if i == 2 {
			time.Sleep(time.Second * 1)
		}
		isAllow := limiter.Allow(context.TODO(), limitKey, tokenFillInterval, bucketSize)
		fmt.Println(i, time.Now(), isAllow)
	}
}

RedisRatelimiter

package main

import (
	"context"
	"fmt"
	"time"

	"gitlab.com/repotea-workspace/gowkit"
	"gitlab.com/repotea-workspace/goratelimiter"
	"github.com/go-redis/redis/v8"
)

func main() {
	rdb, err := gowkit.NewRedisClient(&redis.Options{})
	if err != nil {
		panic(err)
	}

	limiter := ratelimiter.NewRedisRatelimiter(rdb)
	limitKey := "uniq_limit_key"
	tokenFillInterval := time.Second * 1
	bucketSize := 1
	for i := 0; i < 3; i++ {
		// 1st and 3nd is allowed
		if i == 2 {
			time.Sleep(time.Second * 1)
		}
		isAllow := limiter.Allow(context.TODO(), limitKey, tokenFillInterval, bucketSize)
		fmt.Println(i, time.Now(), isAllow)
	}
}

Documentation

Index

Constants

This section is empty.

Variables

View Source
var (
	// MemRatelimiterCacheExpiration MemRatelimiter key 的过期时间
	MemRatelimiterCacheExpiration = time.Minute * 60
	// MemRatelimiterCacheCleanInterval MemRatelimiter 过期 key 的清理时间间隔
	MemRatelimiterCacheCleanInterval = time.Minute * 60
)
View Source
var (
	// RedisRatelimiterCacheExpiration redis ratelimiter 缓存过期时间
	RedisRatelimiterCacheExpiration = time.Minute * 60
)

Functions

func DefaultGinLimitKey

func DefaultGinLimitKey(c *gin.Context) string

DefaultGinLimitKey 使用客户端 IP 生成默认的限频 key

func DefaultGinLimitedHandler

func DefaultGinLimitedHandler(c *gin.Context)

DefaultGinLimitedHandler 限频触发返回 429

func GinMemRatelimiter

func GinMemRatelimiter(conf GinRatelimiterConfig) gin.HandlerFunc

GinMemRatelimiter 按配置信息生成进程内存限频中间件

func GinRedisRatelimiter

func GinRedisRatelimiter(rdb *redis.Client, conf GinRatelimiterConfig) gin.HandlerFunc

GinRedisRatelimiter 按配置信息生成 redis 限频中间件

Types

type GinRatelimiterConfig

type GinRatelimiterConfig struct {
	// LimitKey 生成限频 key 的函数,不传使用默认的对 IP 维度进行限制
	LimitKey func(*gin.Context) string
	// LimitedHandler 触发限频时的 handler
	LimitedHandler func(*gin.Context)
	// TokenBucketConfig 获取 token bucket 每次放入一个token的时间间隔和桶大小配置
	TokenBucketConfig func(*gin.Context) (time.Duration, int)
}

GinRatelimiterConfig Gin Ratelimiter 中间件的配置信息

type MemRatelimiter

type MemRatelimiter struct {
	*rate.Limiter
	*cache.Cache
	Expire time.Duration
}

MemRatelimiter 进程内存 limiter

func NewMemRatelimiter

func NewMemRatelimiter() *MemRatelimiter

NewMemRatelimiter 根据配置信息创建 mem limiter

func (*MemRatelimiter) Allow

func (r *MemRatelimiter) Allow(ctx context.Context, key string, tokenFillInterval time.Duration, bucketSize int) bool

Allow 使用 time/rate 的 token bucket 算法判断给定 key 和对应的限制速率下是否被允许 tokenFillInterval 每隔多长时间往桶中放一个 Token bucketSize 代表 Token 桶的容量大小

type RedisRatelimiter

type RedisRatelimiter struct {
	*redis.Client
	// contains filtered or unexported fields
}

RedisRatelimiter redis limiter

func NewRedisRatelimiter

func NewRedisRatelimiter(rdb *redis.Client) *RedisRatelimiter

NewRedisRatelimiter 根据配置创建 redis limiter

func (*RedisRatelimiter) Allow

func (r *RedisRatelimiter) Allow(ctx context.Context, key string, tokenFillInterval time.Duration, bucketSize int) bool

Allow 判断给定 key 是否被允许

Directories

Path Synopsis

Jump to

Keyboard shortcuts

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