簡單令牌桶實現
阿新 • • 發佈:2021-12-09
主要思路:
1.建立channel,通過定時器定時往channel中寫入令牌,返回令牌桶本身(channel);
2.判斷請求是否可以拿到令牌;
package main import ( "context" "fmt" "time" ) func getBucket(capacityPs, maxCapacity int) (chan struct{}, *time.Ticker) { var bucketToken = make(chan struct{}, maxCapacity) ticker := time.NewTicker(time.Second / time.Duration(capacityPs)) go func() { for { select { case <-ticker.C: bucketToken <- struct{}{} default: } } }() return bucketToken, ticker } func getToken(ctx context.Context, bucket *chan struct{}, wait bool) bool { if wait { select { case <-*bucket: return true case <-ctx.Done(): fmt.Println("請求超時...") return false } } else { // 不阻塞 select { case <-*bucket: return true default: return false } } } // 模擬qps 100, 超時等待時間為1s,模擬1000個請求進來 // 令牌桶最大每秒產生30個令牌,最大突發為50(桶最大容量) func test1() { bucket, ticker := getBucket(30, 50) i := 0 total := 0 fail := 0 go func() { for { // qps 100 i++ time.Sleep(time.Millisecond * 10) ctx, _ := context.WithTimeout(context.Background(), time.Millisecond*100) ok := getToken(ctx, &bucket, false) if ok { total++ } else { fail++ } fmt.Println("當前獲取token是否成功:", ok) if i >= 1000 { ticker.Stop() break } } }() time.Sleep(time.Second * 20) fmt.Println(i, total, fail) } // 模擬qps 100, 超時等待時間為1s,模擬1000個請求進來 // 令牌桶最大每秒產生30個令牌,最大突發為50(桶最大容量) func test2() { bucket, ticker := getBucket(30, 50) i := 0 total := 0 fail := 0 go func() { for { // qps 100 i++ time.Sleep(time.Millisecond * 10) ctx, _ := context.WithTimeout(context.Background(), time.Millisecond*20) ok := getToken(ctx, &bucket, true) if ok { total++ } else { fail++ } fmt.Println("當前獲取token是否成功:", ok) if i >= 1000 { ticker.Stop() break } } }() time.Sleep(time.Second * 20) fmt.Println(i, total, fail) } func main() { //test1() test2() }