package mw import ( "applet/app/e" "applet/app/md" "applet/app/svc" "applet/app/utils" "applet/app/utils/cache" "bytes" "fmt" "github.com/gin-gonic/gin" "io/ioutil" ) func Limiter(c *gin.Context) { limit := 200 // 限流次数 ttl := 2 // 限流过期时间 ip := c.ClientIP() // 读取token或者ip token := c.GetHeader("Authorization") mid := c.GetString("mid") // 判断是否已经超出限额次数 method := c.Request.Method host := c.Request.Host uri := c.Request.URL.String() buf := make([]byte, 5120*10) num, _ := c.Request.Body.Read(buf) body := buf[:num] // Write body back c.Request.Body = ioutil.NopCloser(bytes.NewBuffer(body)) //queryValue := utils.SerializeStr(c.Request.URL.Query()) //不建议开启,失去限流的意义 //TODO::分布式锁阻拦(保证原子性) requestIdPrefix := fmt.Sprintf(md.DealAppLimiterRequestIdPrefix, mid, ip) cb, err := svc.HandleLimiterDistributedLock(mid, ip, requestIdPrefix) if err != nil { e.OutErr(c, e.ERR, err.Error()) return } if cb != nil { defer cb() // 释放锁 } Md5 := utils.Md5(ip + token + method + host + uri + string(body)) //Md5 := utils.Md5(ip + token + method + host + uri + string(body) + queryValue) if cache.Exists(Md5) { c.AbortWithStatusJSON(428, gin.H{ "code": 428, "msg": "don't repeat the request", "data": struct{}{}, }) return } // 2s后没返回自动释放 go cache.SetEx(Md5, "0", ttl) key := "LIMITER_EGG_APP" + ip reqs, _ := cache.GetInt(key) if reqs >= limit { c.AbortWithStatusJSON(429, gin.H{ "code": 429, "msg": "too many requests", "data": struct{}{}, }) return } if reqs > 0 { //go cache.Incr(key) go cache.SetEx(key, reqs+1, ttl) } else { go cache.SetEx(key, 1, ttl) } c.Next() go cache.Del(Md5) }