专业的编程技术博客社区

网站首页 > 博客文章 正文

golang 限流——time/rate(golang限流器)

baijin 2025-03-30 14:16:47 博客文章 12 ℃ 0 评论

限流与熔断降级

限流的意义

高并发系统三大利器: 缓存、降级、限流

缓存: 提升系统访问速度和增大处理容量, 为相应业务增加缓存。

降级: 当服务器压力剧增时, 根据业务策略降级, 以此释放服务资源保证业务正常。

限流: 通过对并发限速, 以达到拒绝服务、排队或等待、降级等处理

限流分类

漏桶限流

每次请求时计算桶流量, 超过阈值则降级请求

令牌桶限流

每次请求时从令牌桶里取令牌, 取不到则降级请求

time/rate 限速器使用(漏桶)

"golang.org/x/time/rate"


rate.NewLimiter(limit, burst)

limit表示每秒产生token数、burst最多存token数

Allow 判断当前是否可以取到token

Wait 阻塞等待直到取到token

Reserve 返回等待时间, 再去取token

【实例】:

package main
import (
    "context"
    "golang.org/x/time/rate"
    "log"
    "testing"
    "time"
)
func Test_RateLimiter(t *testing.T) {
    l := rate.NewLimiter(1, 5)
    log.Println(l.Limit(), l.Burst())
    for i := 0; i < 100; i++ {
        //阻塞等待直到,取到一个token
        log.Println("before Wait")
        c, _ := context.WithTimeout(context.Background(), time.Second*2)
        if err := l.Wait(c); err != nil {
            log.Println("limiter wait err:" + err.Error())
        }
        log.Println("after Wait")
        //返回需要等待多久才有新的token,这样就可以等待指定时间执行任务
        r := l.Reserve()
        log.Println("reserve Delay:", r.Delay())
        //判断当前是否可以取到token
        a := l.Allow()
        log.Println("Allow:", a)
    }
}

测试方法:

在文件所在目录中执行:

go test

测试单个文件

go test -v main_test.go

测试单个函数

go test -v main_test.go -test.run Test_RateLimiter

【实例】:

package main
import (
    "fmt"
    "sync"
    "time"
    //"golang.org/x/time/rate"
)
type Bucket struct {
    cap int64 // 总容量
    lock sync.Mutex // 互斥锁 不需要在初始化中赋值
    tokens int64
    rate int64 // 速率 每秒产生多少token
    lastTime int64 // 记录时间
}
func (b *Bucket) Allow() bool {
    b.lock.Lock()
    defer b.lock.Unlock()
    now := time.Now().Unix() // 当前时间
    // (当前时间 - 上次记录时间)*每秒产生
    b.tokens = b.tokens + (now-b.lastTime)*b.rate
    if b.tokens > b.cap {
        b.tokens = b.cap
    }
    b.lastTime = now
    if b.tokens > 0 {
        return true
    }
    return false
}
// 添加令牌的操作
func (b *Bucket) addToken() {
    b.lock.Lock()
    defer b.lock.Unlock()
    if b.tokens+b.rate <= b.cap 43>
        b.tokens += b.rate
    } else {
        b.tokens = b.cap
    }
    b.lastTime = time.Now().Unix() // 当前时间
}
// 启动一个协程不停的处理
func (b *Bucket) start() { // newBucket的时候 启动start 协程
    for {
        time.Sleep(time.Second)
        b.addToken()
    }
}
func main() {
    // 方法一:原生使用令牌的方法
    /*
    // 创建一个每秒产生5个令牌的令牌桶
    limiter := rate.NewLimiter(5, 1)
    for i := 0; i < 10; i++ {
    // 尝试获取一个令牌
    if limiter.Allow() {
    fmt.Println("处理请求", i)
    } else {
    fmt.Println("限流请求", i)
    }
    // 模拟请求处理时间
    time.Sleep(time.Second)
    }
    */
    // 方法二: 使用协程
    limiter := Bucket{
        cap: 10,
        tokens: 5,
        rate: 1,
    }
    // 使用协程的方式添加表单令牌
    go limiter.start()
    for i := 0; i < 10; i++ {
        // 尝试获取一个令牌
        if limiter.Allow() {
            fmt.Println("处理请求", i)
        } else {
            fmt.Println("限流请求", i)
        }
        // 模拟请求处理时间
        time.Sleep(time.Second)
        }
}

本文暂时没有评论,来添加一个吧(●'◡'●)

欢迎 发表评论:

最近发表
标签列表