golang 限流算法

111 阅读1分钟

固定窗口又称固定窗口(又称计数器算法)

是最简单的限流算法,通过在单位时间内维护的计数器来控制该时间单位内的最大访问量,

易于实现;并且内存占用小,我们只需要存储时间窗口中的计数即可;它能够确保处理更多的最新请求,不会因为旧请求的堆积导致新请求被饿死。当然也面临着临界问题,当两个窗口交界处,瞬时流量可能为2n

图片.png

package main
import (
   "sync"
   "time"
)
type LimitRate struct {
   rate  int           // 阀值
   begin time.Time     // 计数开始时间
   cycle time.Duration // 计数周期
   count int           // 收到的请求数
   lock  sync.Mutex    // 锁
}

func (limit *LimitRate) Allow() bool {
   limit.lock.Lock()
   defer limit.lock.Unlock()

   // 判断收到请求数是否达到阀值
   if limit.count == limit.rate-1 {
      now := time.Now()
      // 达到阀值后,判断是否是请求周期内
      if now.Sub(limit.begin) >= limit.cycle {
         limit.Reset(now)
         return true
      }
      return false
   } else {
      limit.count++
      return true
   }
}


func (limit *LimitRate) Reset(begin time.Time) {
   limit.begin = begin
   limit.count = 0
}

滑动窗口

为了防止瞬时流量,可以把固定窗口近一步划分成多个格子,每次向后移动一小格,而不是固定窗口大小,这就是滑动窗口(Sliding Window)。

图片.png

package main

import (
   "container/list"
   "fmt"
   "sync"
   "time"
)

func main() {
   for {
      time.Sleep(1 * time.Second)
      a := limit(time.Now().Unix())
      fmt.Println(a)
   }
}

var timeWindow int64 = 2
var lock sync.Mutex
var link = list.New()

func limit(nowTime int64) bool {
   lock.Lock()
   defer lock.Unlock()
   if link.Len() < 5 {
      link.PushBack(nowTime)
      return true
   } else {
      //判断最右一个
      if nowTime-link.Back().Value.(int64) > timeWindow {
         link.PushBack(nowTime)
         //删除
         link.Remove(link.Front())
         return true
      } else {
         return false
      }
   }
}

漏桶算法

请求被放在桶里,不会被丢弃,以恒定速度处理,流出 类似消息队列 图片.png

令牌桶算法

请求去拿流量,突发 图片.png