package
0.0.0-20240902184010-10fa96e1e356
Repository: https://github.com/tkcrm/modules.git
Documentation: pkg.go.dev
# README
Limiter
Thit package contains simple limiter
Features
- Sliding window limiter for requests per period
- Register multiple services in one limiter
- It can be used in multiple instances of microservices
- It can be used in multiple stages with one redis
- It can be used with HTTP middleware and custom services logic
- Trade safe instance
- Formated config for enviroment variables: 10-S / 50-M / 100-H / 1000-D
How to use in custom microservice
// define config
limiterCfg := Config{
CachePrefix: "dev-service-name",
}
// init limiter instance
l, err := limiter.New(logger, limiterCfg, redisClient)
if err != nil {
log.Fatal("init limiter error", err)
}
// register new service
if err := l.RegisterServices(
// 50 requests per second
limiter.NewService("test-service", limiter.WithFormattedLimit("50-S")),
// 1000 requests per 10 minutes
limiter.NewService("test-service2", limiter.WithPeriodLimit(time.Minute * 10, 1000)),
); err != nil {
log.Fatal("failed to register services", err)
}
// get registered service
svcLimiter, err := l.GetService(testServiceName)
if err != nil {
log.Fatalf("failed to get service with name %s: %v", testServiceName, err)
}
// get current limit stats
stats, err := svcLimiter.Peek(ctx)
if err != nil {
log.Fatal(err)
}
// return is limit reached
isReached, err := svcLimiter.IsReached(ctx)
if err != nil {
log.Fatal(err)
}
// increment one request and get new stats
stats, err := svcLimiter.Get(ctx)
if err != nil {
log.Fatal(err)
}
// increment custom integer value
stats, err := svcLimiter.Increment(ctx, 10)
if err != nil {
log.Fatal(err)
}
// reset data
stats, err := svcLimiter.Reset(ctx)
if err != nil {
log.Fatal(err)
}
// increment one request and get new stats for user ip
userIP := "192.143.203.16"
stats, err := svcLimiter.Get(ctx, limiter.WithCacheKey(userIP))
if err != nil {
log.Fatal(err)
}
How to use with fiber
func IPRateLimit() fiber.Handler {
// 1. Configure
// define config
limiterCfg := Config{
CachePrefix: "dev-s-apiservice",
}
// init limiter instance
l, err := New(logger, limiterCfg, redisClient)
if err != nil {
log.Fatal("init limiter error", err)
}
// register new service
if err := l.RegisterServices(
limiter.NewService("http-ip-middleware", WithFormattedLimit("50-S")),
); err != nil {
log.Fatal("failed to register services", err)
}
// get registered service
svcLimiter, err := l.GetService("http-ip-middleware")
if err != nil {
log.Fatalf("failed to get service with name %s: %v", "http-ip-middleware", err)
}
// 2. Return middleware handler
return func(c *fiber.Ctx) error {
ctx := c.Context()
limiterCtx, err := svcLimiter.Get(ctx, limiter.WithCacheKey(c.IP()))
if err != nil {
log.Printf("IPRateLimit - ipRateLimiter.Get - err: %v, %s on %s", err, c.IP(), c.Path())
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{
"success": false,
"message": err,
})
}
c.Set("X-RateLimit-Limit", strconv.FormatInt(limiterCtx.Limit, 10))
c.Set("X-RateLimit-Remaining", strconv.FormatInt(limiterCtx.Remaining, 10))
c.Set("X-RateLimit-Reset", strconv.FormatInt(limiterCtx.Reset, 10))
if limiterCtx.Reached {
log.Printf("Too Many Requests from %s on %s", c.IP(), c.Path())
return c.Status(fiber.StatusTooManyRequests).JSON(fiber.Map{
"success": false,
"message": "Too Many Requests on " + c.Path(),
})
}
return c.Next()
}
}
# Functions
No description provided by the author
NewService return new service instance.
No description provided by the author
You can also use the simplified format "<limit>-<period>"", with the given periods:
* "S": second
* "M": minute
* "H": hour
* "D": day
Examples:
* 5 reqs/second: "5-S"
* 10 reqs/minute: "10-M"
* 1000 reqs/hour: "1000-H"
* 2000 reqs/day: "2000-D".
No description provided by the author
# Type aliases
No description provided by the author
ServiceInitOption */.
ServiceMethodOption */.