Simple version implementation of token bucket request frequency limiting.
ratelimiter library that supports in-memory and distributed eventually consistent redis stores (includes Gin middleware)
- lua-ngx-ratelimiter: a token bucket frequency limiting implementation of lua + nginx + redis
- MemRatelimiter: a process memory limiter implemented with rate + go-cache
- RedisRatelimiter: a distributed limiter implemented with redis + lua
- GinMemRatelimiter: encapsulating the MemRatelimiter as a gin middleware
- GinRedisRatelimiter: encapsulating the RedisRatelimiter as a gin middleware
go get -u github.com/axiaoxin-com/ratelimiter
package main
import (
"time"
"github.com/axiaoxin-com/ratelimiter"
"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()
}
package main
import (
"time"
"github.com/axiaoxin-com/goutils"
"github.com/axiaoxin-com/ratelimiter"
"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 := goutils.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()
}
package main
import (
"context"
"fmt"
"time"
"github.com/axiaoxin-com/ratelimiter"
)
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)
}
}
package main
import (
"context"
"fmt"
"time"
"github.com/axiaoxin-com/goutils"
"github.com/axiaoxin-com/ratelimiter"
"github.com/go-redis/redis/v8"
)
func main() {
rdb, err := goutils.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)
}
}