限流与熔断降级
限流的意义
高并发系统三大利器: 缓存、降级、限流
缓存: 提升系统访问速度和增大处理容量, 为相应业务增加缓存。
降级: 当服务器压力剧增时, 根据业务策略降级, 以此释放服务资源保证业务正常。
限流: 通过对并发限速, 以达到拒绝服务、排队或等待、降级等处理
限流分类
漏桶限流
每次请求时计算桶流量, 超过阈值则降级请求
令牌桶限流
每次请求时从令牌桶里取令牌, 取不到则降级请求
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)
}
}
本文暂时没有评论,来添加一个吧(●'◡'●)