想象有一个木桶,系统按照固定速率,例如10ms每次,往桶里加入Token,如果桶已经满了就不再添加。新请求来临时,会各自拿走一个Token,如果没有Token 就拒绝服务。这里如果一段时间没有请求时,桶内就会积累一些token,下次一旦有突发流量,只要token足够,也能一次处理。
总结下令牌桶算法的特点,令牌桶即可以控制进入系统的请求请求量,同时允许突发流量。
在秒杀活动中,用户的请求速率是不固定的,这里我们假定为10r/s,令牌按照5个每秒的速率放入令牌桶,桶中最多存放20个令牌,那系统就只会允许持续的每秒处理5 个请求,或者每隔4 秒,等桶中20 个令牌攒满后,一次处理20个请求的突发情况,保证系统稳定性。
go简易版实现
package main
import (
"fmt"
"time"
)
type Bucket struct {
max int
ch chan int
timer *time.Ticker
}
func NewBucket(n int, sec time.Duration) *Bucket {
return &Bucket{
max: n,
ch: make(chan int, n),
timer: time.NewTicker(sec),
}
}
func (b *Bucket) Get() bool {
select {
case <-b.ch:
return true
default:
return false
}
}
func (b *Bucket) Ticker() {
for i := 0; i < b.max; i++ {
b.ch <- 1
}
for {
select {
case <-b.timer.C:
fmt.Println("len: ", len(b.ch))
for i := len(b.ch); i < b.max; i++ {
b.ch <- 1
}
}
}
}
func main() {
bucket := NewBucket(10, 3*time.Second)
go bucket.Ticker()
for i := 0; i < 6; i++ {
go func(b *Bucket, id int) {
for {
if b.Get() {
fmt.Println("ok: ", id)
} else {
fmt.Println("no: ", id)
}
time.Sleep(2 * time.Second)
}
}(bucket, i)
}
for {
}
}
网友评论