请求限流是一种常见的应用场景,即限制在一定时间内某个接口或服务的请求次数或并发数,以保证系统稳定性和性能。下面我们以Golang为例,介绍如何实现请求限流,主要包括两种方式:计数器法和漏桶算法。

1. 计数器法

计数器法是最简单的请求限流算法,原理是统计在一定时间内某个操作的请求次数,如果超过事先设定的阈值,则拒绝新的请求。下面是一个使用计数器法来限流的例子:

package main

import (
    "fmt"
    "sync"
    "time"
)

type Counter struct {
    count int           // 统计请求数
    mutex sync.Mutex    // 互斥锁
}

func (c *Counter) Incr() {
    c.mutex.Lock()
    defer c.mutex.Unlock()
    c.count++
}

func (c *Counter) Check(max int) bool {
    c.mutex.Lock()
    defer c.mutex.Unlock()
    if c.count > max {
        return false
    }
    return true
}

func main() {
    counter := Counter{}
    max := 100         // 最大请求数
    duration := time.Second   // 统计周期
    t1 := time.Now()
    go func() {
        for {
            if counter.Check(max) {
                time.Sleep(time.Millisecond * 10) // 模拟请求处理时间
                counter.Incr()
            } else {
                fmt.Println("too many requests")
            }
        }
    }()
    for {
        fmt.Println(counter.count)
        time.Sleep(time.Second)
        if time.Since(t1) > duration {
            counter.count = 0
            t1 = time.Now()
            fmt.Println("reset count")
        }
    }
}

代码解释:

– Counter:封装请求数计数器,实现两个方法,Incr()增加请求数,Check()检查请求数是否超过最大数量;
– main函数:启动一个协程,不断发起请求,每次请求时先检查请求数是否超限,如果没有则处理请求,否则输出“too many requests”;同时,定时输出请求数并重置计数器。

2. 漏桶算法

漏桶算法是另一种流量控制算法,主要思想是将请求按照固定的速率处理,多余的请求会被放入漏桶中,而漏桶有一个固定的容量,如果漏桶已满,新请求将被拒绝。下面是一个使用漏桶算法来限流的例子:

package main

import (
    "fmt"
    "sync"
    "time"
)

type LeakyBucket struct {
    capacity   int       // 桶容量
    rate       int       // 固定流速
    water      int       // 当前水位
    lastLeakMs int64     // 上次漏水时间
    mutex      sync.Mutex
}

func (b *LeakyBucket) Pour(inflow int) bool {
    b.mutex.Lock()
    defer b.mutex.Unlock()
    now := time.Now().UnixNano() / int64(time.Millisecond)
    if (now - b.lastLeakMs) > int64(time.Second) {
        b.water = 0
        b.lastLeakMs = now
    }
    b.water += inflow
    if b.water > b.capacity {
        return false
    }
    b.water -= b.rate
    return true
}

func main() {
    bucket := LeakyBucket{capacity: 200, rate: 20}
    inflow := 30   // 请求流量
    duration := time.Second   // 统计周期
    t1 := time.Now()
    go func() {
        for {
            if bucket.Pour(inflow) {
                time.Sleep(time.Millisecond * 10) // 模拟请求处理时间
            } else {
                fmt.Println("too many requests")
            }
        }
    }()
    for {
        fmt.Println(bucket.water)
        time.Sleep(time.Second)
        if time.Since(t1) > duration {
            t1 = time.Now()
            fmt.Println("reset bucket")
        }
    }
}

代码解释:

– LeakyBucket:封装漏桶,实现Pour()方法处理请求,同时做漏水处理;
– main函数:启动一个协程,不断发起请求,每次请求时调用Pour()方法处理,如果无法处理则输出“too many requests”;同时,定时输出漏桶水位并重置漏桶。