limti
This commit is contained in:
parent
df70d6e862
commit
3559f07dfe
5
go.mod
5
go.mod
|
|
@ -4,6 +4,7 @@ go 1.25
|
|||
|
||||
require (
|
||||
github.com/fogleman/gg v1.3.0
|
||||
github.com/go-redis/redis v6.15.9+incompatible
|
||||
github.com/sirupsen/logrus v1.9.3
|
||||
github.com/speps/go-hashids v2.0.0+incompatible
|
||||
gorm.io/gorm v1.31.0
|
||||
|
|
@ -13,7 +14,9 @@ require (
|
|||
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 // indirect
|
||||
github.com/jinzhu/inflection v1.0.0 // indirect
|
||||
github.com/jinzhu/now v1.1.5 // indirect
|
||||
github.com/onsi/ginkgo v1.16.5 // indirect
|
||||
github.com/onsi/gomega v1.40.0 // indirect
|
||||
golang.org/x/image v0.31.0 // indirect
|
||||
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8 // indirect
|
||||
golang.org/x/text v0.29.0 // indirect
|
||||
golang.org/x/text v0.33.0 // indirect
|
||||
)
|
||||
|
|
|
|||
|
|
@ -0,0 +1,92 @@
|
|||
package goutil
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math/rand"
|
||||
"time"
|
||||
|
||||
"github.com/go-redis/redis"
|
||||
)
|
||||
|
||||
type WindowsLimitOption struct {
|
||||
keyPrefix string //redis可以前缀,会拼接上succ和fail
|
||||
limitCount int64 //限制数量
|
||||
scope time.Duration //限制时间区间,默认一分钟
|
||||
succKey string
|
||||
failKey string
|
||||
}
|
||||
type WindowsLimit struct {
|
||||
client *redis.Client
|
||||
option *WindowsLimitOption
|
||||
}
|
||||
|
||||
func NewWindowsLimitOption() *WindowsLimitOption {
|
||||
return &WindowsLimitOption{
|
||||
keyPrefix: fmt.Sprintf("windows:limit:%s:%d", time.Now().Format("20060102"), time.Now().UnixMilli()),
|
||||
limitCount: 1000,
|
||||
scope: time.Second * 60,
|
||||
}
|
||||
}
|
||||
|
||||
func NewWindowsLimit(client *redis.Client, option *WindowsLimitOption) *WindowsLimit {
|
||||
return &WindowsLimit{
|
||||
client: client,
|
||||
option: option,
|
||||
}
|
||||
}
|
||||
|
||||
func (w *WindowsLimit) tidy() {
|
||||
now := time.Now().UnixMilli()
|
||||
start := now - w.option.scope.Milliseconds()
|
||||
|
||||
pipe := w.client.Pipeline()
|
||||
pipe.ZRemRangeByScore(w.option.failKey, "0", fmt.Sprintf("%d", start))
|
||||
pipe.ZRemRangeByScore(w.option.succKey, "0", fmt.Sprintf("%d", start))
|
||||
pipe.Exec()
|
||||
}
|
||||
|
||||
func (w *WindowsLimit) Count() (int64, int64, error) {
|
||||
now := time.Now().UnixMilli()
|
||||
start := now - w.option.scope.Milliseconds()
|
||||
|
||||
pipe := w.client.Pipeline()
|
||||
succCmd := pipe.ZCount(w.option.succKey, fmt.Sprintf("%d", start), fmt.Sprintf("%d", now))
|
||||
failCmd := pipe.ZCount(w.option.failKey, fmt.Sprintf("%d", start), fmt.Sprintf("%d", now))
|
||||
_, err := pipe.Exec()
|
||||
if err != nil {
|
||||
return 0, 0, err
|
||||
}
|
||||
return succCmd.Val(), failCmd.Val(), nil
|
||||
}
|
||||
|
||||
func (w *WindowsLimit) Allow() bool {
|
||||
now := time.Now().UnixMilli()
|
||||
start := now - w.option.scope.Milliseconds()
|
||||
pipe := w.client.Pipeline()
|
||||
succCmd := w.client.ZCount(w.option.succKey, fmt.Sprintf("%d", start), fmt.Sprintf("%d", now))
|
||||
_, err := pipe.Exec()
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
|
||||
if succCmd.Val() >= w.option.limitCount {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func (w *WindowsLimit) Add(succ bool) error {
|
||||
now := time.Now().UnixMilli()
|
||||
w.tidy()
|
||||
pipe := w.client.Pipeline()
|
||||
if succ {
|
||||
pipe.ZAdd(w.option.succKey, redis.Z{Score: float64(now), Member: fmt.Sprintf("%d:%d", now, rand.Int())})
|
||||
} else {
|
||||
pipe.ZAdd(w.option.failKey, redis.Z{Score: float64(now), Member: fmt.Sprintf("%d:%d", now, rand.Int())})
|
||||
}
|
||||
|
||||
pipe.Expire(w.option.failKey, w.option.scope)
|
||||
pipe.Expire(w.option.succKey, w.option.scope)
|
||||
_, err := pipe.Exec()
|
||||
return err
|
||||
}
|
||||
Loading…
Reference in New Issue