深入淺出kubernetes之WorkQueue詳解

{"type":"doc","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"WorkQueue稱爲工作隊列,Kubernetes的WorkQueue隊列與普通FIFO(先進先出,First-In, First-Out)隊列相比,實現略顯複雜,它的主要功能在於標記和去重,並支持如下特性。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"bulletedlist","content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"有序"},{"type":"text","text":":按照添加順序處理元素(item)。"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"去重"},{"type":"text","text":":相同元素在同一時間不會被重複處理,例如一個元素在處理之前被添加了多次,它只會被處理一次。"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"併發性"},{"type":"text","text":":多生產者和多消費者。"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"標記機制"},{"type":"text","text":":支持標記功能,標記一個元素是否被處理,也允許元素在處理時重新排隊。"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"通知機制"},{"type":"text","text":":ShutDown方法通過信號量通知隊列不再接收新的元素,並通知metric goroutine退出。"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"延遲"},{"type":"text","text":":支持延遲隊列,延遲一段時間後再將元素存入隊列。"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"限速"},{"type":"text","text":":支持限速隊列,元素存入隊列時進行速率限制。限制一個元素被重新排隊(Reenqueued)的次數。"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"Metric"},{"type":"text","text":":支持metric監控指標,可用於Prometheus監控。"}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"WorkQueue支持3種隊列,並提供了3種接口,不同隊列實現可應對不同的使用場景,分別介紹如下。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"bulletedlist","content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"Interface"},{"type":"text","text":":FIFO隊列接口,先進先出隊列,並支持去重機制。"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"DelayingInterface"},{"type":"text","text":":延遲隊列接口,基於Interface接口封裝,延遲一段時間後再將元素存入隊列。"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"RateLimitingInterface"},{"type":"text","text":":限速隊列接口,基於DelayingInterface接口封裝,支持元素存入隊列時進行速率限制。"}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"size","attrs":{"size":16}},{"type":"strong"}],"text":"FIFO隊列"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"FIFO隊列支持最基本的隊列方法,例如插入元素、獲取元素、獲取隊列長度等。另外,WorkQueue中的限速及延遲隊列都基於Interface接口實現,其提供如下方法:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"代碼路徑:vendor/k8s.io/client-go/util/workqueue/queue.go"}]},{"type":"codeblock","attrs":{"lang":"go"},"content":[{"type":"text","text":"type Interface interface {\n Add(item interface{})\n Len() int\n Get() (item interface{}, shutdown bool)\n Done(item interface{})\n ShutDown()\n ShuttingDown() bool\n}"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"FIFO隊列Interface方法說明如下。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"bulletedlist","content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"Add"},{"type":"text","text":":給隊列添加元素(item),可以是任意類型元素。"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"Len"},{"type":"text","text":":返回當前隊列的長度。"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"Get"},{"type":"text","text":":獲取隊列頭部的一個元素。"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"Done"},{"type":"text","text":":標記隊列中該元素已被處理。"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"ShutDown"},{"type":"text","text":":關閉隊列。"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"ShuttingDown"},{"type":"text","text":":查詢隊列是否正在關閉。"}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"FIFO隊列數據結構如下:"}]},{"type":"codeblock","attrs":{"lang":"go"},"content":[{"type":"text","text":"type Type struct {\n queue []t\n dirty set\n processing set\n cond *sync.Cond\n shuttingDown bool\n metrics queueMetrics\n unfinishedWorkUpdatePeriod time.Duration\n clock clock.Clock\n}"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"FIFO隊列數據結構中最主要的字段有queue、dirty和processing。其中queue字段是實際存儲元素的地方,它是slice結構的,用於保證元素有序;dirty字段非常關鍵,除了能保證去重,還能保證在處理一個元素之前哪怕其被添加了多次(併發情況下),但也只會被處理一次;processing 字段用於標記機制,標記一個元素是否正在被處理。應根據WorkQueue的特性理解源碼的實現,FIFO存儲過程如圖5-9所示。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/c2/c26387cb10ca582ba63b2243afeac1bd.png","alt":null,"title":null,"style":null,"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":"center","origin":null},"content":[{"type":"text","text":"圖5-9  FIFO存儲過程"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"通過Add方法往FIFO隊列中分別插入1、2、3這3個元素,此時隊列中的queue和dirty字段分別存有1、2、3元素,processing字段爲空。然後通過Get方法獲取最先進入的元素(也就是1元素),此時隊列中的queue和dirty字段分別存有2、3元素,而1元素會被放入processing字段中,表示該元素正在被處理。最後,當我們處理完1元素時,通過Done方法標記該元素已經被處理完成,此時隊列中的processing字段中的1元素會被刪除。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"如圖5-9所示,這是FIFO隊列的存儲流程,在正常的情況下,FIFO隊列運行在併發場景下。高併發下如何保證在處理一個元素之前哪怕其被添加了多次,但也只會被處理一次?下面進行講解,FIFO併發存儲過程如圖5-10所示。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/c7/c7ad23223a54b105e45c4107e747d3ca.png","alt":null,"title":null,"style":null,"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":"center","origin":null},"content":[{"type":"text","text":" 圖5-10  FIFO併發存儲過程"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":"center","origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"如圖5-10所示,在併發場景下,假設goroutine A通過Get方法獲取1元素,1元素被添加到processing字段中,同一時間,goroutine B通過Add方法插入另一個1元素,此時在processing字段中已經存在相同的元素,所以後面的1元素並不會被直接添加到queue字段中,當前FIFO隊列中的dirty字段中存有1、2、3元素,processing字段存有1元素。在goroutine A通過Done方法標記處理完成後,如果dirty字段中存有1元素,則將1元素追加到queue字段中的尾部。需要注意的是,dirty和processing字段都是用Hash Map數據結構實現的,所以不需要考慮無序,只保證去重即可。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"size","attrs":{"size":16}},{"type":"strong"}],"text":"延遲隊列"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"延遲隊列,基於FIFO隊列接口封裝,在原有功能上增加了AddAfter方法,其原理是延遲一段時間後再將元素插入FIFO隊列。延遲隊列數據結構如下:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"代碼路徑:vendor/k8s.io/client-go/util/workqueue/delaying_queue.go"}]},{"type":"codeblock","attrs":{"lang":"go"},"content":[{"type":"text","text":"type DelayingInterface interface {\n Interface\n AddAfter(item interface{}, duration time.Duration)\n}\n\ntype delayingType struct {\n Interface\n clock clock.Clock\n stopCh chan struct{}\n heartbeat clock.Ticker\n waitingForAddCh chan *waitFor\n metrics retryMetrics\n deprecatedMetrics retryMetrics\n}"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"AddAfter方法會插入一個item(元素)參數,並附帶一個duration(延遲時間)參數,該duration參數用於指定元素延遲插入FIFO隊列的時間。如果duration小於或等於0,會直接將元素插入FIFO隊列中。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"delayingType結構中最主要的字段是waitingForAddCh,其默認初始大小爲1000,通過AddAfter方法插入元素時,是非阻塞狀態的,只有當插入的元素大於或等於1000時,延遲隊列纔會處於阻塞狀態。waitingForAddCh字段中的數據通過goroutine運行的waitingLoop函數持久運行。延遲隊列運行原理如圖5-11所示。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/b1/b16ca44c74aa2070e943d261867da534.png","alt":null,"title":null,"style":null,"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":"center","origin":null},"content":[{"type":"text","text":"圖5-11  延遲隊列運行原理"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":"center","origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"如圖5-11所示,將元素1放入waitingForAddCh字段中,通過waitingLoop函數消費元素數據。當元素的延遲時間不大於當前時間時,說明還需要延遲將元素插入FIFO隊列的時間,此時將該元素放入優先隊列(waitForPriorityQueue)中。當元素的延遲時間大於當前時間時,則將該元素插入FIFO隊列中。另外,還會遍歷優先隊列(waitForPriorityQueue)中的元素,按照上述邏輯驗證時間。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"size","attrs":{"size":16}},{"type":"strong"}],"text":"限速隊列"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"限速隊列,基於延遲隊列和FIFO隊列接口封裝,限速隊列接口(RateLimitingInterface)在原有功能上增加了AddRateLimited、Forget、NumRequeues方法。限速隊列的重點不在於RateLimitingInterface接口,而在於它提供的4種限速算法接口(RateLimiter)。其原理是,限速隊列利用延遲隊列的特性,延遲某個元素的插入時間,達到限速目的。RateLimiter數據結構如下:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"代碼路徑:vendor/k8s.io/client-go/util/workqueue/default_rate_limiters.go"}]},{"type":"codeblock","attrs":{"lang":"go"},"content":[{"type":"text","text":"type RateLimiter interface {\n When(item interface{}) time.Duration\n Forget(item interface{})\n NumRequeues(item interface{}) int\n}"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"限速隊列接口方法說明如下。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"bulletedlist","content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"When"},{"type":"text","text":":獲取指定元素應該等待的時間。"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"Forget"},{"type":"text","text":":釋放指定元素,清空該元素的排隊數。"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"NumRequeues"},{"type":"text","text":":獲取指定元素的排隊數。"}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/60/60dfa0099a827284df3b3e9a42e1aa36.png","alt":null,"title":null,"style":null,"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"下面會分別詳解WorkQueue提供的4種限速算法,應對不同的場景,這4種限速算法分別如下。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"bulletedlist","content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"令牌桶算法(BucketRateLimiter)。"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"排隊指數算法(ItemExponentialFailureRateLimiter)。"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"計數器算法(ItemFastSlowRateLimiter)。"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"混合模式(MaxOfRateLimiter),將多種限速算法混合使用。"}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"1. 令牌桶算法"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"令牌桶算法是通過Go語言的第三方庫golang.org/x/time/rate實現的。令牌桶算法內部實現了一個存放token(令牌)的“桶”,初始時“桶”是空的,token會以固定速率往“桶”裏填充,直到將其填滿爲止,多餘的token會被丟棄。每個元素都會從令牌桶得到一個token,只有得到token的元素才允許通過(accept),而沒有得到token的元素處於等待狀態。令牌桶算法通過控制發放token來達到限速目的。令牌桶算法原理如圖5-12所示。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/6a/6a049ef23bcdc8b542bed2782b0d82a7.png","alt":null,"title":null,"style":null,"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":"center","origin":null},"content":[{"type":"text","text":"圖5-12  令牌桶算法原理"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":"center","origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"WorkQueue在默認的情況下會實例化令牌桶,代碼示例如下:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"go"},"content":[{"type":"text","text":"rate.NewLimiter(rate.Limit(10), 100)"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"在實例化rate.NewLimiter後,傳入r和b兩個參數,其中r參數表示每秒往“桶”裏填充的token數量,b參數表示令牌桶的大小(即令牌桶最多存放的token數量)。我們假定r爲10,b爲100。假設在一個限速週期內插入了1000個元素,通過r.Limiter.Reserve().Delay函數返回指定元素應該等待的時間,那麼前b(即100)個元素會被立刻處理,而後面元素的延遲時間分別爲item100/100ms、item101/200ms、item102/300ms、item103/400ms,以此類推。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"2. 排隊指數算法"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"排隊指數算法將相同元素的排隊數作爲指數,排隊數增大,速率限制呈指數級增長,但其最大值不會超過maxDelay。元素的排隊數統計是有限速週期的,一個限速週期是指從執行AddRateLimited方法到執行完Forget方法之間的時間。如果該元素被Forget方法處理完,則清空排隊數。排隊指數算法的核心實現代碼示例如下:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"代碼路徑:vendor/k8s.io/client-go/util/workqueue/default_rate_limiters.go"}]},{"type":"codeblock","attrs":{"lang":"go"},"content":[{"type":"text","text":"r.failures[item] = r.failures[item] + 1\nbackoff := float64(r.baseDelay.Nanoseconds()) * math.Pow(2, float64(exp))\nif backoff > math.MaxInt64 {\n return r.maxDelay\n}"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"該算法提供了3個主要字段:failures、baseDelay、maxDelay。其中,failures字段用於統計元素排隊數,每當AddRateLimited方法插入新元素時,會爲該字段加1;另外,baseDelay字段是最初的限速單位(默認爲5ms),maxDelay字段是最大限速單位(默認爲1000s)。排隊指數增長趨勢如圖5-13所示。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/34/34c1cfab9dfb032236df58ab11d95ce5.png","alt":null,"title":"","style":[{"key":"width","value":"50%"},{"key":"bordertype","value":"none"}],"href":"","fromPaste":false,"pastePass":false}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":"center","origin":null},"content":[{"type":"text","text":"圖5-13  排隊指數增長趨勢"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":"center","origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"限速隊列利用延遲隊列的特性,延遲多個相同元素的插入時間,達到限速目的。"}]},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/ad/adf83563816720d3b0dca4b34491293a.png","alt":null,"title":null,"style":null,"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"我們假定baseDelay是1 * time.Millisecond,maxDelay是1000 * time.Second。假設在一個限速週期內通過AddRateLimited方法插入10個相同元素,那麼第1個元素會通過延遲隊列的AddAfter方法插入並設置延遲時間爲1ms(即baseDelay),第2個相同元素的延遲時間爲2ms,第3個相同元素的延遲時間爲4ms,第4個相同元素的延遲時間爲8ms,第5個相同元素的延遲時間爲16ms……第10個相同元素的延遲時間爲512ms,最長延遲時間不超過1000s(即maxDelay)。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"3. 計數器算法"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"計數器算法是限速算法中最簡單的一種,其原理是:限制一段時間內允許通過的元素數量,例如在1分鐘內只允許通過100個元素,每插入一個元素,計數器自增1,當計數器數到100的閾值且還在限速週期內時,則不允許元素再通過。但WorkQueue在此基礎上擴展了fast和slow速率。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"計數器算法提供了4個主要字段:failures、fastDelay、slowDelay及maxFastAttempts。其中,failures字段用於統計元素排隊數,每當AddRateLimited方法插入新元素時,會爲該字段加1;而fastDelay和slowDelay字段是用於定義fast、slow速率的;另外,maxFastAttempts字段用於控制從fast速率轉換到slow速率。計數器算法核心實現的代碼示例如下:"}]},{"type":"codeblock","attrs":{"lang":"text"},"content":[{"type":"text","text":"r.failures[item] = r.failures[item] + 1\nif r.failures[item] <= r.maxFastAttempts {\n return r.fastDelay\n}\nreturn r.slowDelay"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"假設fastDelay是5 * time.Millisecond,slowDelay是10 * time.Second,maxFastAttempts是3。在一個限速週期內通過AddRateLimited方法插入4個相同的元素,那麼前3個元素使用fastDelay定義的fast速率,當觸發maxFastAttempts字段時,第4個元素使用slowDelay定義的slow速率。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"4. 混合模式"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"混合模式是將多種限速算法混合使用,即多種限速算法同時生效。例如,同時使用排隊指數算法和令牌桶算法,代碼示例如下:"}]},{"type":"codeblock","attrs":{"lang":"go"},"content":[{"type":"text","text":"func DefaultControllerRateLimiter() RateLimiter {\n return NewMaxOfRateLimiter(\n NewItemExponentialFailureRateLimiter(5*time.Millisecond, 1000*time.Second),\n &BucketRateLimiter{Limiter: rate.NewLimiter(rate.Limit(10), 100)},\n )\n}"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/14/144c8a519a432e8d1af766f9fb5cb652.jpeg","alt":null,"title":"","style":[{"key":"width","value":"25%"},{"key":"bordertype","value":"none"}],"href":"https://u.jd.com/3K5DNZ","fromPaste":false,"pastePass":false}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"本文選自《Kubernetes源碼剖析》一書"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"鄭東旭著"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"電子工業出版社出版"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"購買鏈接:https://u.jd.com/3K5DNZ"}]}]}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章