漏桶算法

近期在研究Jaeger,Jaeger中有一種採集策略是速率限制類型,內部使用的是漏桶算法,在這裏研究了下Jaeger漏桶算法的實現原理,自己仿照其實現了一個rateLimiter,並進行了相關測試,下面是主要實現。

  • lck:lck是互斥鎖,主要用來防止併發情況下產生錯誤。
  • rate:速率,即接口每秒限制多少個請求。在這裏也就是水滴從漏桶中流出的速度,同時也是餘量增加的速度。
  • balance:漏桶的空閒餘量,會隨着漏桶滴水逐漸變大;如果將請求添加到漏桶中,會逐漸變小。當請求到來時,如果餘量不足1,那麼表明不能容下當前的請求,當前的請求會被拒絕。
  • limit:漏桶的最大容量。
  • lastTime:上次調用Check函數的時間。用於計算時間差dur,然後計算這段時間漏桶流出的水w,增加的餘量=流出的水量w=時間*速率=dur*rate。

rateLimiter實現代碼:

package ratelimiter

import (
	"sync"
	"time"
)

type rateLimiter struct {
	lck      *sync.Mutex
	rate     float64   //最大速率限制
	balance  float64   //漏桶的餘量
	limit    float64   //漏桶的最大容量限制
	lastTime time.Time //上次檢查的時間
}

func NewRateLimiter(limitPerSecond int, balance int) *rateLimiter {
	return &rateLimiter{
		lck:      new(sync.Mutex),
		rate:     float64(limitPerSecond),
		balance:  float64(balance),
		limit:    float64(balance),
		lastTime: time.Now(),
	}
}

func (r *rateLimiter) Check() bool {
	ok := false
	r.lck.Lock()
	now := time.Now()
	dur := now.Sub(r.lastTime).Seconds()
	r.lastTime = now
	water := dur * r.rate //計算這段時間內漏桶流出水的流量water
	r.balance += water    //漏桶流出water容量的水,自然漏桶的餘量多出water
	if r.balance > r.limit {
		r.balance = r.limit
	}
	if r.balance >= 1 { //漏桶餘量足夠容下當前的請求
		r.balance -= 1
		ok = true
	}
	r.lck.Unlock()
	return ok
}

單元測試代碼:

package ratelimiter

import (
	"fmt"
	"testing"
	"time"
)

func TestRateLimiter_Check(t *testing.T) {
	limiter := NewRateLimiter(10, 1)
	start := time.Now()
	count := 0
	for i := 0; i < 1e3; i++ {
		if limiter.Check() {
			fmt.Println(i)
			count++
		}
		time.Sleep(time.Millisecond)
	}
	fmt.Println("count:", count)
	fmt.Println(time.Now().Sub(start).Seconds())
}

測試結果:每秒限制10個,1.3秒接受了13個請求,加上一開始填滿桶的一個,共14個。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章