91超碰碰碰碰久久久久久综合_超碰av人澡人澡人澡人澡人掠_国产黄大片在线观看画质优化_txt小说免费全本

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

程序員必知的限流方案有哪些

發布時間:2021-10-19 16:25:46 來源:億速云 閱讀:116 作者:iii 欄目:編程語言

這篇文章主要介紹“程序員必知的限流方案有哪些”,在日常操作中,相信很多人在程序員必知的限流方案有哪些問題上存在疑惑,小編查閱了各式資料,整理出簡單好用的操作方法,希望對大家解答”程序員必知的限流方案有哪些”的疑惑有所幫助!接下來,請跟著小編一起來學習吧!

限流簡介

現在說到高可用系統,都會說到高可用的保護手段:緩存、降級和限流,本博文就主要說說限流。限流是流量限速(Rate Limit)的簡稱,是指只允許指定的事件進入系統,超過的部分將被拒絕服務、排隊或等待、降級等處理。

對于server服務而言,限流為了保證一部分的請求流量可以得到正常的響應,總好過全部的請求都不能得到響應,甚至導致系統雪崩。限流與熔斷經常被人弄混,博主認為它們最大的區別在于限流主要在server實現,而熔斷主要在client實現,當然了,一個服務既可以充當server也可以充當client,這也是讓限流與熔斷同時存在一個服務中,這兩個概念才容易被混淆,關注公眾號碼猿技術專欄獲取更多面試資源。

那為什么需要限流呢?很多人第一反應就是服務扛不住了所以需要限流。這是不全面的說法,博主認為限流是因為資源的稀缺或出于安全防范的目的,采取的自我保護的措施。限流可以保證使用有限的資源提供最大化的服務能力,按照預期流量提供服務,超過的部分將會拒絕服務、排隊或等待、降級等處理。

現在的系統對限流的支持各有不同,但是存在一些標準。在HTTP RFC 6585標準中規定了『429 Too Many Requests 』,429狀態碼表示用戶在給定時間內發送了太多的請求,需要進行限流(“速率限制”),同時包含一個 Retry-After 響應頭用于告訴客戶端多長時間后可以再次請求服務,關注公眾號碼猿技術專欄獲取更多面試資源。

HTTP/1.1 429 Too Many Requests
Content-Type: text/html
Retry-After: 3600


  
     <title>Too Many Requests</title>  
  
     <h2>Too Many Requests</h2>     <p>I only allow 50 requests per hour to this Web site per
        logged in user.  Try again soon.</p>

很多應用框架同樣集成了,限流功能并且在返回的Header中給出明確的限流標識。

  • X-Rate-Limit-Limit:同一個時間段所允許的請求的最大數目;

  • X-Rate-Limit-Remaining:在當前時間段內剩余的請求的數量;

  • X-Rate-Limit-Reset:為了得到最大請求數所等待的秒數。

這是通過響應頭告訴調用方服務端的限流頻次是怎樣的,保證后端的接口訪問上限,客戶端也可以根據響應的Header調整請求。

限流分類

限流,拆分來看,就兩個字限和流,限就是動詞限制,很好理解。但是流在不同的場景之下就是不同資源或指標,多樣性就在流中體現。在網絡流量中可以是字節流,在數據庫中可以是TPS,在API中可以是QPS亦可以是并發請求數,在商品中還可以是庫存數... ...但是不管是哪一種『流』,這個流必須可以被量化,可以被度量,可以被觀察到、可以統計出來。我們把限流的分類基于不同的方式分為不同的類別,關注公眾號碼猿技術專欄獲取更多面試資源,如下圖。

程序員必知的限流方案有哪些

限流分類

因為篇幅有限,本文只會挑選幾個常見的類型分類進行說明。

限流粒度分類

根據限流的粒度分類:

  • 單機限流

  • 分布式限流

現狀的系統基本上都是分布式架構,單機的模式已經很少了,這里說的單機限流更加準確一點的說法是單服務節點限流。單機限流是指請求進入到某一個服務節點后超過了限流閾值,服務節點采取了一種限流保護措施。

程序員必知的限流方案有哪些

單機限流示意圖

分布式限流狹義的說法是在接入層實現多節點合并限流,比如NGINX+redis,分布式網關等,廣義的分布式限流是多個節點(可以為不同服務節點)有機整合,形成整體的限流服務。

程序員必知的限流方案有哪些

分布式限流示意圖

單機限流防止流量壓垮服務節點,缺乏對整體流量的感知。分布式限流適合做細粒度不同的限流控制,可以根據場景不同匹配不同的限流規則。與單機限流最大的區別,分布式限流需要中心化存儲,常見的使用redis實現。引入了中心化存儲,就需要解決以下問題:

  • 數據一致性在限流模式中理想的模式為時間點一致性。時間點一致性的定義中要求所有數據組件的數據在任意時刻都是完全一致的,但是一般來說信息傳播的速度最大是光速,其實并不能達到任意時刻一致,總有一定的時間不一致,對于我們CAP中的一致性來說只要達到讀取到最新數據即可,達到這種情況并不需要嚴格的任意時間一致。這只能是理論當中的一致性模型,可以在限流中達到線性一致性即可。

  • 時間一致性這里的時間一致性與上述的時間點一致性不一樣,這里就是指各個服務節點的時間一致性。一個集群有3臺機器,但是在某一個A/B機器的時間為Tue Dec 3 16:29:28 CST 2019,C為Tue Dec 3 16:29:28 CST 2019,那么它們的時間就不一致。那么使用ntpdate進行同步也會存在一定的誤差,對于時間窗口敏感的算法就是誤差點。

  • 超時在分布式系統中就需要網絡進行通信,會存在網絡抖動問題,或者分布式限流中間件壓力過大導致響應變慢,甚至是超時時間閾值設置不合理,導致應用服務節點超時了,此時是放行流量還是拒絕流量?

  • 性能與可靠性分布式限流中間件的資源總是有限的,甚至可能是單點的(寫入單點),性能存在上限。如果分布式限流中間件不可用時候如何退化為單機限流模式也是一個很好的降級方案。

限流對象類型分類

按照對象類型分類:

  • 基于請求限流

  • 基于資源限流

基于請求限流,一般的實現方式有限制總量和限制QPS。限制總量就是限制某個指標的上限,比如搶購某一個商品,放量是10w,那么最多只能賣出10w件。微信的搶紅包,群里發一個紅包拆分為10個,那么最多只能有10人可以搶到,第十一個人打開就會顯示『手慢了,紅包派完了』。

程序員必知的限流方案有哪些

紅包搶完了

限制QPS,也是我們常說的限流方式,只要在接口層級進行,某一個接口只允許1秒只能訪問100次,那么它的峰值QPS只能為100。限制QPS的方式最難的點就是如何預估閾值,如何定位閾值,下文中會說到,關注公眾號碼猿技術專欄獲取更多面試資源。

基于資源限流是基于服務資源的使用情況進行限制,需要定位到服務的關鍵資源有哪些,并對其進行限制,如限制TCP連接數、線程數、內存使用量等。限制資源更能有效地反映出服務當前地清理,但與限制QPS類似,面臨著如何確認資源的閾值為多少。這個閾值需要不斷地調優,不停地實踐才可以得到一個較為滿意地值。

限流算法分類

不論是按照什么維度,基于什么方式的分類,其限流的底層均是需要算法來實現。下面介紹實現常見的限流算法:

  • 計數器

  • 令牌桶算法

  • 漏桶算法

計數器

固定窗口計數器

計數限流是最為簡單的限流算法,日常開發中,我們說的限流很多都是說固定窗口計數限流算法,比如某一個接口或服務1s最多只能接收1000個請求,那么我們就會設置其限流為1000QPS。該算法的實現思路非常簡單,維護一個固定單位時間內的計數器,如果檢測到單位時間已經過去就重置計數器為零。

程序員必知的限流方案有哪些

固定窗口計數器原理

其操作步驟:

  1. 時間線劃分為多個獨立且固定大小窗口;

  2. 落在每一個時間窗口內的請求就將計數器加1;

  3. 如果計數器超過了限流閾值,則后續落在該窗口的請求都會被拒絕。但時間達到下一個時間窗口時,計數器會被重置為0。

下面實現一個簡單的代碼。

package limitimport (
   "sync/atomic"   "time")

type Counter struct {
   Count       uint64   // 初始計數器   Limit       uint64  // 單位時間窗口最大請求頻次   Interval    int64   // 單位ms   RefreshTime int64   // 時間窗口}func NewCounter(count, limit uint64, interval, rt int64) *Counter {
   return &Counter{
      Count:       count,
      Limit:       limit,
      Interval:    interval,
      RefreshTime: rt,
   }
}func (c *Counter) RateLimit() bool {
   now := time.Now().UnixNano() / 1e6
   if now < (c.RefreshTime + c.Interval) {
      atomic.AddUint64(&c.Count, 1)
      return c.Count <= c.Limit   } else {
      c.RefreshTime = now
      atomic.AddUint64(&c.Count, -c.Count)
      return true   }
}

測試代碼:

package limitimport (
   "fmt"   "testing"   "time")func Test_Counter(t *testing.T) {
   counter := NewCounter(0, 5, 100, time.Now().Unix())
   for i := 0; i < 10; i++ {
      go func(i int) {
         for k := 0; k <= 10; k++ {
            fmt.Println(counter.RateLimit())
            if k%3 == 0 {
               time.Sleep(102 * time.Millisecond)
            }
         }
      }(i)
   }
   time.Sleep(10 * time.Second)
}

看了上面的邏輯,有沒有覺得固定窗口計數器很簡單,對,就是這么簡單,這就是它的一個優點實現簡單。同時也存在兩個比較嚴重缺陷。試想一下,固定時間窗口1s限流閾值為100,但是前100ms,已經請求來了99個,那么后續的900ms只能通過一個了,就是一個缺陷,基本上沒有應對突發流量的能力。第二個缺陷,在00:00:00這個時間窗口的后500ms,請求通過了100個,在00:00:01這個時間窗口的前500ms還有100個請求通過,對于服務來說相當于1秒內請求量達到了限流閾值的2倍。

滑動窗口計數器

滑動時間窗口算法是對固定時間窗口算法的一種改進,這詞被大眾所知實在TCP的流量控制中。固定窗口計數器可以說是滑動窗口計數器的一種特例,滑動窗口的操作步驟:

  1. 將單位時間劃分為多個區間,一般都是均分為多個小的時間段;

  2. 每一個區間內都有一個計數器,有一個請求落在該區間內,則該區間內的計數器就會加一;

  3. 每過一個時間段,時間窗口就會往右滑動一格,拋棄最老的一個區間,并納入新的一個區間;

  4. 計算整個時間窗口內的請求總數時會累加所有的時間片段內的計數器,計數總和超過了限制數量,則本窗口內所有的請求都被丟棄。

時間窗口劃分的越細,并且按照時間"滑動",這種算法避免了固定窗口計數器出現的上述兩個問題。缺點是時間區間的精度越高,算法所需的空間容量就越大。

常見的實現方式主要有基于redis zset的方式和循環隊列實現。基于redis zset可將Key為限流標識ID,Value保持唯一,可以用UUID生成,Score 也記為同一時間戳,最好是納秒級的。使用redis提供的 ZADD、EXPIRE、ZCOUNT 和 zremrangebyscore 來實現,并同時注意開啟 Pipeline 來盡可能提升性能。實現很簡單,但是缺點就是zset的數據結構會越來越大。

漏桶算法

漏桶算法是水先進入到漏桶里,漏桶再以一定的速率出水,當流入水的數量大于流出水時,多余的水直接溢出。把水換成請求來看,漏桶相當于服務器隊列,但請求量大于限流閾值時,多出來的請求就會被拒絕服務。漏桶算法使用隊列實現,可以以固定的速率控制流量的訪問速度,可以做到流量的“平整化”處理。

大家可以通過網上最流行的一張圖來理解。

程序員必知的限流方案有哪些

漏桶算法原理

漏桶算法實現步驟:

  1. 將每個請求放入固定大小的隊列進行存儲;

  2. 隊列以固定速率向外流出請求,如果隊列為空則停止流出;

  3. 如隊列滿了則多余的請求會被直接拒絕·

漏桶算法有一個明顯的缺陷:當短時間內有大量的突發請求時,即使服務器負載不高,每個請求也都得在隊列中等待一段時間才能被響應。

令牌桶算法

令牌桶算法的原理是系統會以一個恒定的速率往桶里放入令牌,而如果請求需要被處理,則需要先從桶里獲取一個令牌,當桶里沒有令牌可取時,則拒絕服務。從原理上看,令牌桶算法和漏桶算法是相反的,前者為“進”,后者為“出”。

漏桶算法與令牌桶算法除了“方向”上的不同還有一個更加主要的區別:令牌桶算法限制的是平均流入速率(允許突發請求,只要有足夠的令牌,支持一次拿多個令牌),并允許一定程度突發流量;

令牌桶算法的實現步驟:

  1. 令牌以固定速率生成并放入到令牌桶中;

  2. 如果令牌桶滿了則多余的令牌會直接丟棄,當請求到達時,會嘗試從令牌桶中取令牌,取到了令牌的請求可以執行;

  3. 如果桶空了,則拒絕該請求。

程序員必知的限流方案有哪些

令牌桶算法原理

四種策略該如何選擇?

  • 固定窗口:實現簡單,但是過于粗暴,除非情況緊急,為了能快速止損眼前的問題可以作為臨時應急的方案。

  • 滑動窗口:限流算法簡單易實現,可以應對有少量突增流量場景。

  • 漏桶:對于流量絕對均勻有很強的要求,資源的利用率上不是極致,但其寬進嚴出模式,保護系統的同時還留有部分余量,是一個通用性方案。

  • 令牌桶:系統經常有突增流量,并盡可能的壓榨服務的性能。

怎么做限流?

不論使用上述的哪一種分類或者實現方式,系統都會面臨一個共同的問題:如何確認限流閾值。有人團隊根據經驗先設定一個小的閾值,后續慢慢進行調整;有的團隊是通過進行壓力測試后總結出來。這種方式的問題在于壓測模型與線上環境不一定一致,接口的單壓不能反饋整個系統的狀態,全鏈路壓測又難以真實反應實際流量場景流量比例。

再換一個思路是通過壓測+各應用監控數據。根據系統峰值的QPS與系統資源使用情況,進行等水位放大預估限流閾值,問題在于系統性能拐點未知,單純的預測不一定準確甚至極大偏離真實場景。正如《Overload Control for Scaling WeChat Microservices》所說,在具有復雜依賴關系的系統中,對特定服務的進行過載控制可能對整個系統有害或者服務的實現有缺陷。

希望后續可以出現一個更加AI的運行反饋自動設置限流閾值的系統,可以根據當前QPS、資源狀態、RT情況等多種關聯數據動態地進行過載保護。

不論是哪一種方式給出的限流閾值,系統都應該關注以下幾點:

  1. 運行指標狀態,比如當前服務的QPS、機器資源使用情況、數據庫的連接數、線程的并發數等;

  2. 資源間的調用關系,外部鏈路請求、內部服務之間的關聯、服務之間的強弱依賴等;

  3. 控制方式,達到限流后對后續的請求直接拒絕、快速失敗、排隊等待等處理方式

go限流類庫使用

限流的類庫有很多,不同語言的有不同的類庫,如大Java的有concurrency-limits、Sentinel、Guava 等,這些類庫都有很多的分析和使用方式了,本文主要介紹Golang的限流類庫就是Golang的擴展庫:

https://github.com/golang/time/rate

可以進去語言類庫的代碼都值得去研讀一番,學習過Java的同學是否對AQS的設計之精妙而感嘆呢! time/rate 也有其精妙的部分,下面開始進入類庫學習階段。

github.com/golang/time/rate

進行源碼分析前的,最應該做的是了解類庫的使用方式、使用場景和API。對業務有了初步的了解,閱讀代碼就可以事半功倍。因為篇幅有限后續的博文在對多個限流類庫源碼做分析。

類庫的API文檔:

https://godoc.org/golang.org/x/time/rate%E3%80%82

time/rate類庫是基于令牌桶算法實現的限流功能。前面說令牌桶算法的原理是系統會以一個恒定的速率往桶里放入令牌,那么桶就有一個固定的大小,往桶中放入令牌的速率也是恒定的,并且允許突發流量。查看文檔發現一個函數:

func NewLimiter(r Limit, b int) *Limiter

newLimiter返回一個新的限制器,它允許事件的速率達到r,并允許最多突發b個令牌。也就是說Limter限制時間的發生頻率,但這個桶一開始容量就為b,并且裝滿b個令牌(令牌池中最多有b個令牌,所以一次最多只能允許b個事件發生,一個事件花費掉一個令牌),然后每一個單位時間間隔(默認1s)往桶里放入r個令牌。

limter := rate.NewLimiter(10, 5)

上面的例子表示,令牌桶的容量為5,并且每一秒中就往桶里放入10個令牌。細心的讀者都會發現函數NewLimiter第一個參數是Limit類型,可以看源碼就會發現Limit實際上就是float64的別名。

// Limit defines the maximum frequency of some events.// Limit is represented as number of events per second.// A zero Limit allows no events.type Limit float64

限流器還可以指定往桶里放入令牌的時間間隔,實現方式如下:

limter := rate.NewLimiter(rate.Every(100*time.Millisecond), 5)

這兩個例子的效果是一樣的,使用第一種方式不會出現在每一秒間隔一下子放入10個令牌,也是均勻分散在100ms的間隔放入令牌。rate.Limiter提供了三類方法用來限速:

  • Allow/AllowN

  • Wait/WaitN

  • Reserve/ReserveN

下面對比這三類限流方式的使用方式和適用場景。先看第一類方法:

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

Allow 是AllowN(time.Now(), 1)的簡化方法。那么重點就在方法 AllowN上了,API的解釋有點抽象,說得云里霧里的,可以看看下面的API文檔解釋:

AllowN reports whether n events may happen at time now. Use this method if you intend to drop / skip events that exceed the rate limit. 
Otherwise use Reserve or Wait.

實際上就是為了說,方法 AllowN在指定的時間時是否可以從令牌桶中取出N個令牌。也就意味著可以限定N個事件是否可以在指定的時間同時發生。這個兩個方法是無阻塞,也就是說一旦不滿足,就會跳過,不會等待令牌數量足夠才執行。

也就是文檔中的第二行解釋,如果打算丟失或跳過超出速率限制的時間,那么久請使用該方法。比如使用之前實例化好的限流器,在某一個時刻,服務器同時收到超過了8個請求,如果令牌桶內令牌小于8個,那么這8個請求就會被丟棄。一個小示例:

func AllowDemo() {
   limter := rate.NewLimiter(rate.Every(200*time.Millisecond), 5)
   i := 0   for {
      i++
      if limter.Allow() {
         fmt.Println(i, "====Allow======", time.Now())
      } else {
         fmt.Println(i, "====Disallow======", time.Now())
      }
      time.Sleep(80 * time.Millisecond)
      if i == 15 {
         return      }
   }
}

執行結果:

1 ====Allow====== 2019-12-14 15:54:09.9852178 +0800 CST m=+0.0059980012 ====Allow====== 2019-12-14 15:54:10.1012231 +0800 CST m=+0.1220033013 ====Allow====== 2019-12-14 15:54:10.1823056 +0800 CST m=+0.2030858014 ====Allow====== 2019-12-14 15:54:10.263238 +0800 CST m=+0.2840182015 ====Allow====== 2019-12-14 15:54:10.344224 +0800 CST m=+0.3650042016 ====Allow====== 2019-12-14 15:54:10.4242458 +0800 CST m=+0.4450260017 ====Allow====== 2019-12-14 15:54:10.5043101 +0800 CST m=+0.5250903018 ====Allow====== 2019-12-14 15:54:10.5852232 +0800 CST m=+0.6060034019 ====Disallow====== 2019-12-14 15:54:10.6662181 +0800 CST m=+0.68699830110 ====Disallow====== 2019-12-14 15:54:10.7462189 +0800 CST m=+0.76699910111 ====Allow====== 2019-12-14 15:54:10.8272182 +0800 CST m=+0.84799840112 ====Disallow====== 2019-12-14 15:54:10.9072192 +0800 CST m=+0.92799940113 ====Allow====== 2019-12-14 15:54:10.9872224 +0800 CST m=+1.00800260114 ====Disallow====== 2019-12-14 15:54:11.0672253 +0800 CST m=+1.08800550115 ====Disallow====== 2019-12-14 15:54:11.1472946 +0800 CST m=+1.168074801

第二類方法:因為ReserveN比較復雜,第二類先說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)的簡化方法。與AllowN不同的是WaitN會阻塞,如果令牌桶內的令牌數不足N個,WaitN會阻塞一段時間,阻塞時間的時長可以用第一個參數ctx進行設置,把 context 實例為context.WithDeadline或context.WithTimeout進行制定阻塞的時長。

func WaitNDemo() {
   limter := rate.NewLimiter(10, 5)
   i := 0   for {
      i++
      ctx, canle := context.WithTimeout(context.Background(), 400*time.Millisecond)
      if i == 6 {
         // 取消執行         canle()
      }
      err := limter.WaitN(ctx, 4)

      if err != nil {
         fmt.Println(err)
         continue      }
      fmt.Println(i, ",執行:", time.Now())
      if i == 10 {
         return      }
   }
}

執行結果:

1 ,執行:2019-12-14 15:45:15.538539 +0800 CST m=+0.0110234012 ,執行:2019-12-14 15:45:15.8395195 +0800 CST m=+0.3120039013 ,執行:2019-12-14 15:45:16.2396051 +0800 CST m=+0.7120895014 ,執行:2019-12-14 15:45:16.6395169 +0800 CST m=+1.1120013015 ,執行:2019-12-14 15:45:17.0385893 +0800 CST m=+1.511073701context canceled7 ,執行:2019-12-14 15:45:17.440514 +0800 CST m=+1.9129984018 ,執行:2019-12-14 15:45:17.8405152 +0800 CST m=+2.3129996019 ,執行:2019-12-14 15:45:18.2405402 +0800 CST m=+2.71302460110 ,執行:2019-12-14 15:45:18.6405179 +0800 CST m=+3.113002301

適用于允許阻塞等待的場景,比如消費消息隊列的消息,可以限定最大的消費速率,過大了就會被限流避免消費者負載過高。

第三類方法:

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

與之前的兩類方法不同的是Reserve/ReserveN返回了Reservation實例。Reservation在API文檔中有5個方法:

func (r *Reservation) Cancel() // 相當于CancelAt(time.Now())func (r *Reservation) CancelAt(now time.Time)func (r *Reservation) Delay() time.Duration // 相當于DelayFrom(time.Now())func (r *Reservation) DelayFrom(now time.Time) time.Durationfunc (r *Reservation) OK() bool

通過這5個方法可以讓開發者根據業務場景進行操作,相比前兩類的自動化,這樣的操作顯得復雜多了。通過一個小示例來學習Reserve/ReserveN:

func ReserveNDemo() {
   limter := rate.NewLimiter(10, 5)
   i := 0   for {
      i++
      reserve := limter.ReserveN(time.Now(), 4)
      // 如果為flase說明拿不到指定數量的令牌,比如需要的令牌數大于令牌桶容量的場景      if !reserve.OK() {
         return      }
      ts := reserve.Delay()
      time.Sleep(ts)
      fmt.Println("執行:", time.Now(),ts)
      if i == 10 {
         return      }
   }
}

執行結果:

執行:2019-12-14 16:22:26.6446468 +0800 CST m=+0.008000201 0s執行:2019-12-14 16:22:26.9466454 +0800 CST m=+0.309998801 247.999299ms執行:2019-12-14 16:22:27.3446473 +0800 CST m=+0.708000701 398.001399ms執行:2019-12-14 16:22:27.7456488 +0800 CST m=+1.109002201 399.999499ms執行:2019-12-14 16:22:28.1456465 +0800 CST m=+1.508999901 398.997999ms執行:2019-12-14 16:22:28.5456457 +0800 CST m=+1.908999101 399.0003ms執行:2019-12-14 16:22:28.9446482 +0800 CST m=+2.308001601 399.001099ms執行:2019-12-14 16:22:29.3446524 +0800 CST m=+2.708005801 399.998599ms執行:2019-12-14 16:22:29.7446514 +0800 CST m=+3.108004801 399.9944ms執行:2019-12-14 16:22:30.1446475 +0800 CST m=+3.508000901 399.9954ms

如果在執行Delay()之前操作Cancel()那么返回的時間間隔就會為0,意味著可以立即執行操作,不進行限流。

func ReserveNDemo2() {
   limter := rate.NewLimiter(5, 5)
   i := 0   for {
      i++
      reserve := limter.ReserveN(time.Now(), 4)
      // 如果為flase說明拿不到指定數量的令牌,比如需要的令牌數大于令牌桶容量的場景      if !reserve.OK() {
         return      }

      if i == 6 || i == 5 {
         reserve.Cancel()
      }
      ts := reserve.Delay()
      time.Sleep(ts)
      fmt.Println(i, "執行:", time.Now(), ts)
      if i == 10 {
         return      }
   }
}

執行結果:

1 執行:2019-12-14 16:25:45.7974857 +0800 CST m=+0.007005901 0s2 執行:2019-12-14 16:25:46.3985135 +0800 CST m=+0.608033701 552.0048ms3 執行:2019-12-14 16:25:47.1984796 +0800 CST m=+1.407999801 798.9722ms4 執行:2019-12-14 16:25:47.9975269 +0800 CST m=+2.207047101 799.0061ms5 執行:2019-12-14 16:25:48.7994803 +0800 CST m=+3.009000501 799.9588ms6 執行:2019-12-14 16:25:48.7994803 +0800 CST m=+3.009000501 0s7 執行:2019-12-14 16:25:48.7994803 +0800 CST m=+3.009000501 0s8 執行:2019-12-14 16:25:49.5984782 +0800 CST m=+3.807998401 798.0054ms9 執行:2019-12-14 16:25:50.3984779 +0800 CST m=+4.607998101 799.0075ms10 執行:2019-12-14 16:25:51.1995131 +0800 CST m=+5.409033301 799.0078ms

看到這里time/rate的限流方式已經完成,除了上述的三類限流方式,time/rate還提供了動態調整限流器參數的功能。相關API如下:

func (lim *Limiter) SetBurst(newBurst int) // 相當于SetBurstAt(time.Now(), newBurst).func (lim *Limiter) SetBurstAt(now time.Time, newBurst int)// 重設令牌桶的容量func (lim *Limiter) SetLimit(newLimit Limit) // 相當于SetLimitAt(time.Now(), newLimit)func (lim *Limiter) SetLimitAt(now time.Time, newLimit Limit)// 重設放入令牌的速率

這四個方法可以讓程序根據自身的狀態動態的調整令牌桶速率和令牌桶容量。

到此,關于“程序員必知的限流方案有哪些”的學習就結束了,希望能夠解決大家的疑惑。理論與實踐的搭配能更好的幫助大家學習,快去試試吧!若想繼續學習更多相關知識,請繼續關注億速云網站,小編會繼續努力為大家帶來更多實用的文章!

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

茌平县| 晋中市| 化德县| 靖西县| 额敏县| 巴青县| 石台县| 拜泉县| 揭阳市| 天台县| 昌宁县| 中阳县| 桐城市| 安陆市| 安西县| 安泽县| 南陵县| 太康县| 东乌| 兰西县| 长海县| 兰溪市| 贺州市| 茂名市| 淮安市| 承德市| 武威市| 和平县| 赤峰市| 牟定县| 上饶市| 曲松县| 恩平市| 叶城县| 文昌市| 武邑县| 禹城市| 临西县| 台前县| 白朗县| 琼结县|