什么是限流

限流是指经过必定的算法,对接口的恳求进行约束,防止并发量过大,导致体系瘫痪或响应变慢的状况出现。

为什么要进行限流

在高并发的场景下,假如不进行限流,体系可能会由于过多的恳求而溃散。限流能够维护体系免于被流量打崩,从而保证体系的可用性和稳定性。

Gin结构的限流完成

Gin 是一个根据 Go 言语的 web 结构,它供给了很多便利的中间件,能够便利地完成限流。

以下是一个根据 Gin 完成的令牌桶限流的例子:

  1. 定义令牌桶结构体

    Copy code
    type TokenBucket struct {
        capacity  int64   // 桶的容量
        rate      float64 // 令牌放入速率
        tokens    float64 // 当前令牌数量
        lastToken time.Time // 上一次放令牌的时刻
        mtx       sync.Mutex // 互斥锁
    }
    
  2. 完成令牌桶算法

    func (tb *TokenBucket) Allow() bool {
        tb.mtx.Lock()
        defer tb.mtx.Unlock()
        now := time.Now()
        // 计算需求放的令牌数量
        tb.tokens = tb.tokens + tb.rate*now.Sub(tb.lastToken).Seconds()
        if tb.tokens > float64(tb.capacity) {
            tb.tokens = float64(tb.capacity)
        }
        // 判断是否答应恳求
        if tb.tokens >= 1 {
            tb.tokens--
            tb.lastToken = now
            return true
        } else {
            return false
        }
    }
    
  3. 运用中间件进行限流

    func LimitHandler(maxConn int) gin.HandlerFunc {
        tb := &TokenBucket{
            capacity:  maxConn,
            rate:      1.0,
            tokens:    0,
            lastToken: time.Now(),
        }
        return func(c *gin.Context) {
            if !tb.Allow() {
                c.String(503, "Too many request")
                c.Abort()
                return
            }
            c.Next()
        }
    }
    
  4. 在路由中运用中间件

    r := gin.Default()
    // 在路由中运用中间件
    r.Use(LimitHandler(100))
    r.GET("/", func(c *gin.Context) {
        c.String(200, "Hello, World!")
    })
    r.Run(":8080")
    

以上代码完成了一个简略的令牌桶限流中间件,能够约束最大并发连接数为 100。假如超过了这个连接数,将会回来 503 状态码。

测验

浏览器地址栏输入http://localhost:8080/, 然后张狂刷新即可.

  • 测验截图

Gin 框架限流实现

总结

总的来说,运用 Gin 结构进行限流是一个便利有效的方法,能够进步体系的可用性和稳定性,防止由于过多的恳求导致体系溃散的问题。运用令牌桶算法完成限流能够很好地控制恳求的并发量,能够经过控制桶容量和放入速率等参数进行调理和优化。在运用中间件进行限流时,应该根据实际应用场景和需求调理限流参数,祝您的应用愉快运行!

完整代码

package main
import (
   "github.com/gin-gonic/gin"
   "sync"
   "time"
)
type TokenBucket struct {
   capacity  int64      // 桶的容量
   rate      float64    // 令牌放入速率
   tokens    float64    // 当前令牌数量
   lastToken time.Time  // 上一次放令牌的时刻
   mtx       sync.Mutex // 互斥锁
}
func (tb *TokenBucket) Allow() bool {
   tb.mtx.Lock()
   defer tb.mtx.Unlock()
   now := time.Now()
   // 计算需求放的令牌数量
   tb.tokens = tb.tokens + tb.rate*now.Sub(tb.lastToken).Seconds()
   if tb.tokens > float64(tb.capacity) {
      tb.tokens = float64(tb.capacity)
   }
   // 判断是否答应恳求
   if tb.tokens >= 1 {
      tb.tokens--
      tb.lastToken = now
      return true
   } else {
      return false
   }
}
func LimitHandler(maxConn int64) gin.HandlerFunc {
   tb := &TokenBucket{
      capacity:  maxConn,
      rate:      1.0,
      tokens:    0,
      lastToken: time.Now(),
   }
   return func(c *gin.Context) {
      if !tb.Allow() {
         c.String(503, "Too many request")
         c.Abort()
         return
      }
      c.Next()
   }
}
func main() {
   r := gin.Default()
   // 在路由中运用中间件
   r.Use(LimitHandler(100))
   r.GET("/", func(c *gin.Context) {
      c.String(200, "Hello, World!")
   })
   r.Run(":8080")
}