<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          Golang官方限流器的用法詳解

          共 4791字,需瀏覽 10分鐘

           ·

          2021-08-17 05:42

          點擊上方“Go語言進階學習”,進行關注

          回復“Go語言”即可獲贈從入門到進階共10本電子書

          主人下馬客在船,舉酒欲飲無管弦。

          限流器是提升服務穩(wěn)定性的非常重要的組件,可以用來限制請求速率,保護服務,以免服務過載。限流器的實現(xiàn)方法有很多種,常見的限流算法有固定窗口、滑動窗口、漏桶、令牌桶,我在前面的文章 常用限流算法的應用場景和實現(xiàn)原理 中給大家講解了這幾種限流方法自身的特點和應用場景,其中令牌桶在限流的同時還可以應對一定的突發(fā)流量,與互聯(lián)網(wǎng)應用容易因為熱點事件出現(xiàn)突發(fā)流量高峰的特點更契合。

          簡單來說,令牌桶就是想象有一個固定大小的桶,系統(tǒng)會以恒定速率向桶中放 Token,桶滿則暫時不放。在請求比較的少的時候桶可以先"攢"一些Token,應對突發(fā)的流量,如果桶中有剩余 Token 就可以一直取。如果沒有剩余 Token,則需要等到桶中被放置了 Token 才行。

          關于令牌桶限流更詳細的解釋請參考文章:常用限流算法的應用場景和實現(xiàn)原理

          有的同學在看明白令牌桶的原理后就非常想去自己實現(xiàn)一個限流器應用到自己的項目里,em... 怎么說呢,造個輪子確實有利于自己水平提高,不過要是應用到商用項目里的話其實大可不必自己去造輪子,Golang官方已經替我們造好輪子啦 ......~!

          Golang 官方提供的擴展庫里就自帶了限流算法的實現(xiàn),即 golang.org/x/time/rate。該限流器也是基于 Token Bucket(令牌桶) 實現(xiàn)的。

          限流器的內部結構

          time/rate包的Limiter類型對限流器進行了定義,所有限流功能都是通過基于Limiter類型實現(xiàn)的,其內部結構如下:

          type Limiter struct {
           mu     sync.Mutex
           limit  Limit
           burst  int // 令牌桶的大小
           tokens float64
           last time.Time // 上次更新tokens的時間
           lastEvent time.Time // 上次發(fā)生限速器事件的時間(通過或者限制都是限速器事件)
          }

          其主要字段的作用是:

          • limit:limit字段表示往桶里放Token的速率,它的類型是Limit,是int64的類型別名。設置limit時既可以用數(shù)字指定每秒向桶中放多少個Token,也可以指定向桶中放Token的時間間隔,其實指定了每秒放Token的個數(shù)后就能計算出放每個Token的時間間隔了。
          • burst: 令牌桶的大小。
          • tokens: 桶中的令牌。
          • last: 上次往桶中放 Token 的時間。
          • lastEvent:上次發(fā)生限速器事件的時間(通過或者限制都是限速器事件)

          可以看到在 timer/rate 的限流器實現(xiàn)中,并沒有單獨維護一個 Timer 和隊列去真的每隔一段時間向桶中放令牌,而是僅僅通過計數(shù)的方式表示桶中剩余的令牌。每次消費取 Token 之前會先根據(jù)上次更新令牌數(shù)的時間差更新桶中Token數(shù)。

          大概了解了time/rate限流器的內部實現(xiàn)后,下面的內容我們會集中介紹下該組件的具體使用方法:

          構造限流器

          我們可以使用以下方法構造一個限流器對象:

          limiter := rate.NewLimiter(10100);

          這里有兩個參數(shù):

          1. 第一個參數(shù)是 r Limit,設置的是限流器Limiter的limit字段,代表每秒可以向 Token 桶中產生多少 token。Limit 實際上是 float64 的別名。
          2. 第二個參數(shù)是 b int,b 代表 Token 桶的容量大小,也就是設置的限流器 Limiter 的burst字段。

          那么,對于以上例子來說,其構造出的限流器的令牌桶大小為 100, 以每秒 10 個 Token 的速率向桶中放置 Token。

          除了給r Limit參數(shù)直接指定每秒產生的 Token 個數(shù)外,還可以用 Every 方法來指定向桶中放置 Token 的間隔,例如:

          limit := rate.Every(100 * time.Millisecond);
          limiter := rate.NewLimiter(limit, 100);

          以上就表示每 100ms 往桶中放一個 Token。本質上也是一秒鐘往桶里放 10 個。

          使用限流器

          Limiter 提供了三類方法供程序消費 Token,可以每次消費一個 Token,也可以一次性消費多個 Token。每種方法代表了當 Token 不足時,各自不同的對應手段,可以阻塞等待桶中Token補充,也可以直接返回取Token失敗。

          Wait/WaitN

          func (lim *Limiter) Wait(ctx context.Context) (err error)
          func (lim *Limiter) WaitN(ctx context.Context, n int) (err error)

          Wait 實際上就是 WaitN(ctx,1)

          當使用 Wait 方法消費 Token 時,如果此時桶內 Token 數(shù)組不足 (小于 N),那么 Wait 方法將會阻塞一段時間,直至 Token 滿足條件。如果充足則直接返回。

          這里可以看到,Wait 方法有一個 context 參數(shù)。我們可以設置 context 的 Deadline 或者 Timeout,來決定此次 Wait 的最長時間。

          // 一直等到獲取到桶中的令牌
          err := limiter.Wait(context.Background())
          if err != nil {
           fmt.Println("Error: ", err)
          }

          // 設置一秒的等待超時時間
          ctx, _ := context.WithTimeout(context.Background(), time.Second * 1)
          err := limiter.Wait(ctx)
          if err != nil {
           fmt.Println("Error: ", err)
          }

          Allow/AllowN

          func (lim *Limiter) Allow() bool
          func (lim *Limiter) AllowN(now time.Time, n int) bool

          Allow 實際上就是對 AllowN(time.Now(),1) 進行簡化的函數(shù)。

          AllowN 方法表示,截止到某一時刻,目前桶中數(shù)目是否至少為 n 個,滿足則返回 true,同時從桶中消費 n 個 token。反之不消費桶中的Token,返回false。

          對應線上的使用場景是,如果請求速率超過限制,就直接丟棄超頻后的請求。

          if limiter.AllowN(time.Now(), 2) {
              fmt.Println("event allowed")
          else {
              fmt.Println("event not allowed")
          }

          Reserve/ReserveN

          func (lim *Limiter) Reserve() *Reservation
          func (lim *Limiter) ReserveN(now time.Time, n int) *Reservation

          Reserve 相當于 ReserveN(time.Now(), 1)

          ReserveN 的用法就相對來說復雜一些,當調用完成后,無論 Token 是否充足,都會返回一個 *Reservation 對象。你可以調用該對象的Delay()方法,該方法返回的參數(shù)類型為time.Duration,反映了需要等待的時間,必須等到等待時間之后,才能進行接下來的工作。如果不想等待,可以調用Cancel()方法,該方法會將 Token 歸還。

          舉一個簡單的例子,我們可以這么使用 Reserve 方法。

          r := limiter.Reserve()
          f !r.OK() {
              // Not allowed to act! Did you remember to set lim.burst to be > 0 ?
              return
          }
          time.Sleep(r.Delay())
          Act() // 執(zhí)行相關邏輯

          動態(tài)調整速率和桶大小

          Limiter 支持創(chuàng)建后動態(tài)調整速率和桶大?。?/p>

          1. SetLimit(Limit) 改變放入 Token 的速率
          2. SetBurst(int) 改變 Token 桶大小

          有了這兩個方法,可以根據(jù)現(xiàn)有環(huán)境和條件以及我們的需求,動態(tài)地改變 Token 桶大小和速率。

          總結

          今天我們總結了 Golang 官方限流器的使用方法,它是一種令牌桶算實現(xiàn)的限流器。其中 Wait/WaitN,Allow/AllowN 這兩組方法在平時用的比較多,前者是消費Token時如果桶中Token不足可以讓程序等待桶中新Token的放入(最好設置上等待時長)后者則是在桶中的Token不足時選擇直接丟棄請求。

          除了Golang官方提供的限流器實現(xiàn),Uber公司開源的限流器uber-go/ratelimit也是一個很好的選擇,與Golang官方限流器不同的是Uber的限流器是通過漏桶算法實現(xiàn)的,不過對傳統(tǒng)的漏桶算法進行了改良,有興趣的同學可以自行去體驗一下。

          ------------------- End -------------------

          往期精彩文章推薦:

          歡迎大家點贊,留言,轉發(fā),轉載,感謝大家的相伴與支持

          想加入Go學習群請在后臺回復【入群

          萬水千山總是情,點個【在看】行不行

          瀏覽 125
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  国产三级片视频网站 | 激情无码网站 | 人人操人人摸人人操 | 五月天亚洲综合小说网 | 国产日本视频完整版无删减在线观看 |