package lxlimit import ( "apigame/util/util-lx/lxconv" "encoding/json" "fmt" "github.com/astaxie/beego/logs" redigo "github.com/garyburd/redigo/redis" "hash/fnv" "strconv" "strings" ) // api限流 // 创建时间:2023/11/24 11:23 // 创建人:lixu type LimitConfig struct { Redis MapRedisConfig `json:"redis"` Func MFuncConfig `json:"func"` } type MFuncItem struct { Name string `json:"name"` Times int `json:"times"` // 每几秒 Nums int `json:"nums"` // 限制多少次 } type MFuncConfig struct { FunName string `json:"fun_name"` Open bool `json:"open"` Ip MFuncItem `json:"ip"` Kernel MFuncItem `json:"kernel"` BodyBy []byte `json:"bodyBy"` Url string `json:"url"` Params map[string]string `json:"params"` DelGameIDS []string `json:"delGameIDS"` // 删除某个游戏 Member MFuncItem `json:"member"` } type LimitVar struct { RedisConfig MapRedisConfig RedisPool *redigo.Pool FuncConfig MFuncConfig } var LXLIMITCL LimitVar // InitDefault 初始化 func InitDefault(cfg LimitConfig) (err error) { err = initRedis(cfg.Redis) if err != nil { logs.Error("InitDefault", "initRedis error::", err) return } LXLIMITCL.FuncConfig = cfg.Func return } // CheckLimit 检查是否超 出限制 func CheckLimit(cfg MFuncConfig) (gameid, tp string) { var ( times = 0 postdata map[string]interface{} ) postdata = make(map[string]interface{}) if len(cfg.BodyBy) > 0 { _ = json.Unmarshal([]byte(cfg.BodyBy), &postdata) if postdata["gameid"] != nil { gameid = lxconv.ToString(postdata["gameid"]) } } if !LXLIMITCL.FuncConfig.Open || cfg.FunName == "" { return } if len(cfg.DelGameIDS) > 0 { if cfg.Url != "" { for _, gameid := range cfg.DelGameIDS { if strings.Contains(cfg.Url, gameid) { tp = "gameid" break } } if tp != "" { return } } if postdata["gameid"] != nil { pgameid := lxconv.ToString(postdata["gameid"]) for _, gid := range cfg.DelGameIDS { if pgameid == gid { tp = "gameid" break } } if tp != "" { return } } } if cfg.Ip.Name != "" { if cfg.Ip.Times == 0 { cfg.Ip.Times = LXLIMITCL.FuncConfig.Ip.Times } if cfg.Ip.Nums == 0 { cfg.Ip.Nums = LXLIMITCL.FuncConfig.Ip.Nums } } if cfg.Kernel.Name != "" { if cfg.Kernel.Times == 0 { cfg.Kernel.Times = LXLIMITCL.FuncConfig.Kernel.Times } if cfg.Kernel.Nums == 0 { cfg.Kernel.Nums = LXLIMITCL.FuncConfig.Kernel.Nums } } if cfg.Member.Times == 0 { cfg.Member.Times = LXLIMITCL.FuncConfig.Member.Times } if cfg.Member.Nums == 0 { cfg.Member.Nums = LXLIMITCL.FuncConfig.Member.Nums } _rds := LXredis{} ipif := (cfg.Ip.Times > 0 && cfg.Ip.Name != "") if ipif { _rds.Name = "ip::" + cfg.Ip.Name _rds.Name += getRedisKey(cfg.FunName, cfg.Ip.Name) // ip if isx, _ := _rds.IsExist(); isx { gstr, _ := _rds.GET() if gstr != "" { times, _ = strconv.Atoi(gstr) if times >= cfg.Ip.Nums { tp = "ip" return } } _rds.INCR() //logs.Error("ip:::11::", lxconv.JsonEncode(cfg.Ip)) } else { //logs.Error("ip:::22::", lxconv.JsonEncode(cfg.Ip)) _rds.Val = "1" _rds.setTime(cfg.Ip.Times) } ex, _ := _rds.TTL() if ex < 0 { _rds.EXPIRE(cfg.Ip.Times) } } if ipif && cfg.Kernel.Times > 0 && cfg.Kernel.Name != "" { _rds.Name = "kernel::" + cfg.Ip.Name _rds.Name += getRedisKey(cfg.FunName, cfg.Ip.Name, cfg.Kernel.Name) // ip if isx, _ := _rds.IsExist(); isx { gstr, _ := _rds.GET() if gstr != "" { times, _ = strconv.Atoi(gstr) if times >= cfg.Kernel.Nums { tp = "kernel" return } } _rds.INCR() //logs.Error("Kernel:::11::", lxconv.JsonEncode(cfg.Kernel)) } else { _rds.Val = "1" _rds.setTime(cfg.Kernel.Times) //logs.Error("Kernel:::22::", lxconv.JsonEncode(cfg.Kernel)) } ex, _ := _rds.TTL() if ex < 0 { _rds.EXPIRE(cfg.Kernel.Times) } } var uid string if cfg.Member.Name != "" { if strings.Contains(cfg.Member.Name, "{gameid}") && postdata["gameid"] != nil { gameid = lxconv.InterfaceToStr(postdata["gameid"]) cfg.Member.Name = strings.Replace(cfg.Member.Name, "{gameid}", gameid, -1) } if strings.Contains(cfg.Member.Name, "{uid}") && postdata["uid"] != nil { uid = lxconv.InterfaceToStr(postdata["uid"]) cfg.Member.Name = strings.Replace(cfg.Member.Name, "{uid}", uid, -1) } } if cfg.Member.Times > 0 && cfg.Member.Name != "" { _rds.Name = "member::" + cfg.Member.Name _rds.Name += getRedisKey(cfg.FunName) // ip if isx, _ := _rds.IsExist(); isx { gstr, _ := _rds.GET() if gstr != "" { times, _ = strconv.Atoi(gstr) if times >= cfg.Member.Nums { tp = "member" return } } _rds.INCR() //logs.Error("Member:::11::", lxconv.JsonEncode(cfg.Member)) } else { _rds.Val = "1" _rds.setTime(cfg.Member.Times) //logs.Error("Member:::22::", lxconv.JsonEncode(cfg.Member)) } ex, _ := _rds.TTL() if ex < 0 { _rds.EXPIRE(cfg.Member.Times) } } return } func hashMod(key string, modulus int) int { // 使用FNV哈希算法计算哈希值 hash := fnv.New32a() hash.Write([]byte(key)) hashValue := hash.Sum32() // 对哈希值取模得到结果 result := int(hashValue % uint32(modulus)) return result } func getRedisKey(ccs ...interface{}) (k string) { var pms string for _, cc := range ccs { pms += lxconv.InterfaceToStr(cc) } mds := hashMod(pms, 100000) k = "::" + lxconv.EncryMD5(pms) k += fmt.Sprintf("%d", mds) return }