這是一個建立於 的文章,其中的資訊可能已經有所發展或是發生改變。
官方連結
介面介紹
type Limiter
type Limiter struct { // contains filtered or unexported fields}
Limter限制時間的發生頻率,採用令牌池的演算法實現。這個池子一開始容量為b,裝滿b個令牌,然後每秒往裡面填充r個令牌。
由於令牌池中最多有b個令牌,所以一次最多隻能允許b個事件發生,一個事件花費掉一個令牌。
Limter提供三中主要的函數 Allow, Reserve, and Wait. 大部分時候使用Wait。
func NewLimiter
func NewLimiter(r Limit, b int) *Limiter
NewLimiter 返回一個新的Limiter。
func (*Limiter) [Allow]
func (lim *Limiter) Allow() bool
Allow 是函數 AllowN(time.Now(), 1)的簡化函數。
func (*Limiter) AllowN
func (lim *Limiter) AllowN(now time.Time, n int) bool
AllowN標識在時間now的時候,n個事件是否可以同時發生(也意思就是now的時候是否可以從令牌池中取n個令牌)。如果你需要在事件超出頻率的時候丟棄或跳過事件,就使用AllowN,否則使用Reserve或Wait.
func (*Limiter) Reserve
func (lim *Limiter) Reserve() *Reservation
Reserve是ReserveN(time.Now(), 1).的簡化形式。
func (*Limiter) ReserveN
func (lim *Limiter) ReserveN(now time.Time, n int) *Reservation
ReserveN 返回對象Reservation ,標識調用者需要等多久才能等到n個事件發生(意思就是等多久令牌池中至少含有n個令牌)。
如果ReserveN 傳入的n大於令牌池的容量b,那麼返回false.
使用範例如下:
r := lim.ReserveN(time.Now(), 1)if !r.OK() { // Not allowed to act! Did you remember to set lim.burst to be > 0 ?我只要1個事件發生仍然返回false,是不是b設定為了0? return}time.Sleep(r.Delay())Act()
如果希望根據頻率限制等待和降低事件發生的速度而不丟掉事件,就使用這個方法。
我認為這裡要表達的意思就是如果事件發生的頻率是可以由調用者控制的話,可以用ReserveN 來控制事件發生的速度而不丟掉事件。如果要使用context的到期日或cancel方法的話,使用WaitN。
func (*Limiter) Wait
func (lim *Limiter) Wait(ctx context.Context) (err error)
Wait是WaitN(ctx, 1)的簡化形式。
func (*Limiter) WaitN
func (lim *Limiter) WaitN(ctx context.Context, n int) (err error)
WaitN 阻塞當前直到lim允許n個事件的發生。
- 如果n超過了令牌池的容量大小則報錯。
- 如果Context被取消了則報錯。
- 如果lim的等待時間超過了Context的逾時時間則報錯。
測試 AllowN
package mainimport ( "os" "time" "golang.org/x/time/rate" "github.com/op/go-logging")var log = logging.MustGetLogger("example")// Example format string. Everything except the message has a custom color// which is dependent on the log level. Many fields have a custom output// formatting too, eg. the time returns the hour down to the milli second.var format = logging.MustStringFormatter( `%{color}%{time:15:04:05.000} %{shortfunc} %{level:.4s} %{id:03x}%{color:reset} %{message}`,)func main() { backend1 := logging.NewLogBackend(os.Stderr, "", 0) backend2 := logging.NewLogBackend(os.Stderr, "", 0) backend2Formatter := logging.NewBackendFormatter(backend2, format) backend1Leveled := logging.AddModuleLevel(backend1) backend1Leveled.SetLevel(logging.ERROR, "") logging.SetBackend(backend1Leveled, backend2Formatter) r := rate.Every(1) limit := rate.NewLimiter(r, 10) for { if limit.AllowN(time.Now(), 8) { log.Info("log:event happen") } else { log.Info("log:event not allow") } }}
測試ReserveN
參考YY哥
package mainimport ( "bytes" "fmt" "io" "time" "golang.org/x/time/rate")type reader struct { r io.Reader limiter *rate.Limiter}// Reader returns a reader that is rate limited by// the given token bucket. Each token in the bucket// represents one byte.func NewReader(r io.Reader, l *rate.Limiter) io.Reader { return &reader{ r: r, limiter:l, }}func (r *reader) Read(buf []byte) (int, error) { n, err := r.r.Read(buf) if n <= 0 { return n, err } now := time.Now() rv := r.limiter.ReserveN(now, n) if !rv.OK() { return 0, fmt.Errorf("Exceeds limiter's burst") } delay := rv.DelayFrom(now) //fmt.Printf("Read %d bytes, delay %d\n", n, delay) time.Sleep(delay) return n, err}func main() { // Source holding 1MB src := bytes.NewReader(make([]byte, 1024*1024)) // Destination dst := &bytes.Buffer{} // Bucket adding 100KB every second, holding max 100KB limit := rate.NewLimiter(100*1024, 100*1024) start := time.Now() buf := make([]byte, 10*1024) // Copy source to destination, but wrap our reader with rate limited one //io.CopyBuffer(dst, NewReader(src, limit), buf) r := NewReader(src, limit) for{ if n, err := r.Read(buf); err == nil { dst.Write(buf[0:n]) }else{ break } } fmt.Printf("Copied %d bytes in %s\n", dst.Len(), time.Since(start))}
後記
之前一直在CSDN上寫文章,後面會逐步轉換到簡書上,還請大家多多支援。