一、ip限流器
1.借助使用golang 令牌桶,它可以指定桶的大小和每秒流进桶的个数。
type Limiter struct {
limiter rate.Limiter
lastTime time.Time
key string
}
func (l *Limiter) Allow() bool {
return l.limiter.Allow()
}
2.对不同的ip使用不同的限流器,将ip-limiter对存储到sync.map中
type Limiters struct {
limiters sync.Map
}
func NewLimiters() *Limiters {
ls := &Limiters{}
go ls.cleanLimiter()
return ls
}
func (ls *Limiters) GetLimiter(key string, r rate.Limit, b int) *Limiter {
if v, ok := ls.limiters.Load(key); ok {
l := v.(*Limiter)
l.lastTime = time.Now()
return l
}
l := &Limiter{
limiter: *rate.NewLimiter(r, b),
lastTime: time.Now(),
key: key,
}
ls.limiters.Store(key, l)
return l
}
3.同时对不再访问的限流器,应该定时的清除
func (ls *Limiters) cleanLimiter() {
ticker := time.NewTicker(time.Second * 10)
defer ticker.Stop()
for {
<-ticker.C
ls.limiters.Range(func(key, value interface{}) bool {
l := value.(*Limiter)
if time.Now().Sub(l.lastTime) > time.Minute {
ls.limiters.Delete(key)
}
return true
})
}
}
4.构建中间件:每个IP只能每秒访问一次。
var limiters = NewLimiters()
func LimitMiddleWare(ctx *gin.Context) {
ip := ctx.RemoteIP()
l := limiters.GetLimiter(ip, rate.Limit(1), 1)
if !l.Allow() {
ctx.Status(http.StatusTooManyRequests)
ctx.Abort()
}
}
5.测试:将中间件放在全局。当然也可以放在一个路由上或一个路由组上。
func main() {
en := gin.New()
en.Use(middleware.LimitMiddleWare)
en.GET("/index", func(ctx *gin.Context) {
ctx.String(200, "index")
})
en.Run(":8080")
}
快速请求两次,第二次被拦截!
二、崩溃recover器
1.recover必须在defer中执行。将debug.Stack()写入文件中。
func MyRecover() gin.HandlerFunc {
return func(c *gin.Context) {
defer func() {
if e := recover(); e != nil {
pwd, _ := os.Getwd()
fd, e2 := os.Create(pwd + "/" + time.Now().Format("20060102030405") + ".stack")
if e2 == nil && fd != nil {
e3, ok := e.(error)
if ok {
fd.WriteString("recover:" + e3.(error).Error() + "\n")
}
fd.Write(debug.Stack())
fd.Close()
} else {
log.Println("error:", e, e2)
log.Println("error:", string(debug.Stack()))
}
}
}()
c.Next() // 调用下一个处理
}
}