- 取得連結
- X
- 以電子郵件傳送
- 其他應用程式
程式語言:Go
功能:限制單位時間內的工作次數
- Package:
- rate
功能:限制單位時間內的工作次數
演算法描述
該限流器是基於 Token Bucket(令牌桶) 實現的
簡單來說,令牌桶就是想像有一個固定大小 b 的桶,系統會以恆定速率 r 向桶中放 Token,桶滿則不放
而用戶則從桶中取 Token,如果有剩餘 Token 就可以一直取。
如果沒有剩餘 Token,則需要等到桶中被放置了 Token 才行
簡單來說,令牌桶就是想像有一個固定大小 b 的桶,系統會以恆定速率 r 向桶中放 Token,桶滿則不放
而用戶則從桶中取 Token,如果有剩餘 Token 就可以一直取。
如果沒有剩餘 Token,則需要等到桶中被放置了 Token 才行
建立 Limiter
- func NewLimiter(r Limit, b int) *Limiter
- 建立 Limiter
- 一開始 Token 桶中全滿,速率可能會超過 limit r,之後逐漸下降至 r
- 參數
- r: 每秒向 Token 桶中產生多少 Token
- b: Token 桶的容量大小,決定瞬間最大可取量
- 用法
每秒產生 0.5 個 Token,放進容量為 1 的 Token 桶limiter := NewLimiter(0.5, 1)
- func Every(interval time.Duration) Limit
- 每隔幾秒投入 Token,可用來產生 Limiter 的參數 r
- 參數
- interval: 產生的間隔時間
- 用法
每天產生 50 個 Token,放進容量為 1 的 Token 桶rt := rate.Every(24*time.Hour / 50) limiter := rate.NewLimiter(rt, 1)
Limiter 設定值
- func (lim *Limiter) Burst() int
- 得到目前 Token 桶的容量,相當於瞬間最大可取量
- func (lim *Limiter) Limit() Limit
- 得到目前每秒產生的 Token 數目
- func (lim *Limiter) SetBurst(newBurst int)
- 等同 SetBurstAt(time.Now(), newBurst)
- func (lim *Limiter) SetBurstAt(now time.Time, newBurst int)
- 指定時間設定目前 Token 桶的容量,相當於瞬間最大可取量
- 調用之前尚未執行的操作可能會違反或未充分利用新的 Burst
- func (lim *Limiter) SetLimit(newLimit Limit)
- 等同 SetLimitAt(time.Now(), newLimit)
- func (lim *Limiter) SetLimitAt(now time.Time, newLimit Limit)
- 指定時間設定目前每秒產生的 Token 數目
- 調用之前尚未執行的操作可能會違反或未充分利用新的 Limit
操作(皆會消耗對應數目的 Token)
- 常用操作為 Wait
若需要保留等之後再處理,可用 Reserve
若需要等待並由 context 控制去留,可用 Wait
若需要丟棄或跳過超出速率限制的事件,可用 Allow - func (lim *Limiter) Allow() bool
- 等同 AllowN(time.Now(), 1)
- func (lim *Limiter) AllowN(now time.Time, n int) bool
- 是否可以同時提供 n 個 Token
- func (lim *Limiter) Wait(ctx context.Context) (err error)
- 等同 WaitN(ctx, 1)
- func (lim *Limiter) WaitN(ctx context.Context, n int) (err error)
- 一直等待直到可提供 n 個 Token,會造成 block
- error 的狀況
- n 大於 Token 桶的容量大小,也就是大於瞬間最大可取量
- 例外:當 r 為 rate.Inf,則忽略突發限制
- context.Canceled
- context.DeadlineExceeded
- func (lim *Limiter) Reserve() *Reservation
- 等同 ReserveN(time.Now(), 1)
- func (lim *Limiter) ReserveN(now time.Time, n int) *Reservation
- 保留 n 個 Token
如果 n 大於 Token 桶的容量大小,也就是大於瞬間最大可取量
r.OK() 會回傳 false - 範例
r := lim.ReserveN(time.Now(), 1) if !r.OK() { // Not allowed to act! Did you remember to set lim.burst to be > 0 ? return } time.Sleep(r.Delay()) Act()
Reservation 對應 function
- func (r *Reservation) Cancel()
- 等同 CancelAt(time.Now())
- func (r *Reservation) CancelAt(now time.Time)
- 在特定時間取消保留,會將保留的 Token 歸還
- func (r *Reservation) Delay() time.Duration
- 等同 DelayFrom(time.Now())
- func (r *Reservation) DelayFrom(now time.Time) time.Duration
- 在特定時間後,需等待多少時間才能得到指定數量的 Token
- 若回傳 rate.InfDuration 表示無法在最大等待時間內得到指定數量的 Token
- func (r *Reservation) OK() bool
- 確定保留是否 OK
如果為 false,則 Delay 返回 InfDuration,而 Cancel 不執行任何操作
範例程式碼
The Go Playground
package main import ( "context" "fmt" "log" "time" "golang.org/x/time/rate" ) var _ rate.Limit func main() { const ( n = 10 tf = 5 * time.Second b = 1 ) lim := rate.NewLimiter(rate.Every(tf/n), b) // _ = lim.ReserveN(time.Now(), b) // <<<--- 可試著不註解,看看會發生什麼事 fmt.Println("expected limit:", lim.Limit()) fmt.Println("completed elapsed actual rate") ctx := context.Background() start := time.Now() for i := 0; i < 2*n+2; i++ { err := lim.Wait(ctx) if err != nil { log.Fatal(err) } elapsed := time.Since(start) completed := i + 1 actual := float64(completed) / elapsed.Seconds() fmt.Printf("%8d %8v %v\n", completed, elapsed, actual) } }
留言
張貼留言