This page looks best with JavaScript enabled

Kubernetes RateLimite work queue 設計真d不錯

 ·  ☕ 5 min read

首先本文所以 source code 基於 kubernetes 1.19 版本,所有 source code 的為了版面的整潔會精簡掉部分 log 相關的程式碼,僅保留核心邏輯,如果有見解錯誤的地方,還麻煩觀看本文的大大們提出,感謝!

kubernetes work queue

在前一篇 kubernetes delaying work queue 設計真d不錯一文中分享 kubernetes work queue最基礎的實作方式,再複習一次!Kubernetes 為什麼要實踐一個 work queue 呢?

就我們所知 kubernetes 是用 go 撰寫應該可以使用 channel 的機制直接將物件送給要用的元件(thread)啊,原因其實非常簡單,go channel 的設計功能非常單一無法滿足 kubernetes 所要的場景,例如帶有延遲時間物件需要根據延遲時間排序的queue,例如限制物件取出速度的queue。

圖片來源:How to Create a Kubernetes Custom Controller Using client-go

上圖引用了How to Create a Kubernetes Custom Controller Using client-go的 controller 架構圖可以看到在 sharedindexinformer 內有引用到這個元件,這個元件實際被定義在 kubernetes 的 client-go library 中。

在第二步驟與第三步驟之間透過 queue 不只解偶了上下層的耦合關係同時 Queue 有達到了消峰填谷的作用,當觀察的物件一直送資料進來不會因為我們業務邏輯處理得太慢而卡住,資料會保留在 queue 中直到被取出。

之前有提到了兩種 queue ,分別是 rate limiters queue 以及 delaying queue ,上一章節介紹完 Kubernetes delaying queue ,本篇文章將介紹最後一個 rate limiters queue !

RateLimiter queue

kubernetes source code 設計得非常精美,我們可以先從 interface 定義了哪些方法來推敲實作這個 interface 的物件可能有什麼功能。

interface

source code

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
// RateLimitingInterface is an interface that rate limits items being added to the queue.
//RateLimiting interface 結合了 delaying work queue ,透過一些方法計算 物件的延遲速率
// 再交由 delaying work queue 丟入Heap 最後放入 common queue中。
type RateLimitingInterface interface {
	DelayingInterface        // delaying work queue 前一篇有介紹過

	// AddRateLimited adds an item to the workqueue after the rate limiter says it's ok
	AddRateLimited(item interface{})    // 對某個物件加入延遲

	// Forget indicates that an item is finished being retried.  Doesn't matter whether it's for perm failing
	// or for success, we'll stop the rate limiter from tracking it.  This only clears the `rateLimiter`, you
	// still have to call `Done` on the queue.
	Forget(item interface{})    //  表示某個物件已經做了

	// NumRequeues returns back how many times the item was requeued
	NumRequeues(item interface{}) int //計算某個物件調用queueAddRateLimited的次數
}

看完了抽象的定義之後,必須要回過來看 delaying queue 實際物件定義了哪些屬性

struct

source code

1
2
3
4
5
6
7
// rateLimitingType wraps an Interface and provides rateLimited re-enquing
type rateLimitingType struct {
	DelayingInterface      // 嵌入 delaying work queue 表示 rateLimitingType 也有 delay work queue的能力
                              //(不一定需要傳入實作的物件,本身就是DelayingInterface的一種)
                             
	rateLimiter RateLimiter     //組合了一個 RateLimiter interface,需要明確傳入實作的物件。
}

剛剛上面有一個疑點那就 type RateLimiter 到底是什麼
我們先來看看 type RateLimiter 的結構

RateLimiter

由於 rateLimitingType 組合了一個 RateLimiter 我們來看看 RateLimiter 是什麼吧!
source code

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
type RateLimiter interface {
	// When gets an item and gets to decide how long that item should wait
    //當一個物件放入的時候,需要回傳延遲多久(可自定義規則,等等會看到)
	When(item interface{}) time.Duration
    
    
	// Forget indicates that an item is finished being retried.  Doesn't matter whether its for perm failing	    
    // or for success, we'll stop tracking it
    //當一個物件完成的時候可以,要忘記曾經延遲過(重新計算)
    Forget(item interface{})
    
    
	// NumRequeues returns back how many failures the item has had
    // 回傳物件已經放入幾次(重試了幾次,白話一點呼叫whem幾次)
	NumRequeues(item interface{}) int
}

看到這裡想必大家一定亂了,我幫大家整理一下個類別之間的關係。

看圖說故事的時間到了
Interface 繼承關係

1. Interface 為 common work queue 的介面(先用介面表示抽象方法,這裡就不用Interface會搞混....)

2. DelayingInterface 為 delaying work queue 的介面,這個介面繼承了Interface

3. RateLimitingInterface 繼承了 delaying work queue 的介面

rateLimitingType 有什麼能力

- rateLimitingType 嵌入(embedding)了 DelayingInterface ,表示 `rateLimitingType` 有 DelayingInterface 的能力(用嵌入委託給其他人)

- rateLimitingType 實作了 RateLimitingInterface ,表示 `rateLimitingType` 有 RateLimitingInterface 的能力(這個能力要自己實作)

rateLimitingType 依賴

- 由於 rateLimitingType 組合了(Composite) rateLimiter ,所以在 UML 上呈現的是依賴 rateLimiter 
- rateLimiter 被 ItemExponentialFailureRateLimiter 實作(還由其他物件實作,我這裡沒畫出來)

struct function

rateLimitingType 實作了 RateLimitingInterface 我們來看一下他實作了什麼

source code

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20

// AddRateLimited AddAfter's the item based on the time when the rate limiter says it's ok
// rateLimitingType 本身實作了 RateLimitingInterface 
func (q *rateLimitingType) AddRateLimited(item interface{}) {
    // 由於嵌入了 DelayingInterface 可以直接使用 delay work queue的抽象方法
    // 這裡就是說放入的物件要延遲多久,延遲多久就由組合的 rateLimiter 抽象方法實作。
	q.DelayingInterface.AddAfter(item, q.rateLimiter.When(item))
}

//  rateLimitingType 本身實作了 RateLimitingInterface
func (q *rateLimitingType) NumRequeues(item interface{}) int {
    // 這裡就是說放入的物件已經被重複放入多少次了,放入多少次就由組合的 rateLimiter 抽象方法實作。
	return q.rateLimiter.NumRequeues(item)
}

//  rateLimitingType 本身實作了 RateLimitingInterface
func (q *rateLimitingType) Forget(item interface{}) {
    // 這裡就是說放入的物件已經被做完了,可以重新計算了,從新計算的方法就交給組合的 rateLimiter 抽象方法實作。
	q.rateLimiter.Forget(item)
}

其實看到這裡,我覺得 rateLimitingType 有點像是 proxy 設計模式,只要有人實作了 RateLimiter 就可以放進來給上層做使,反正上層只會呼叫 AddRateLimited 、NumRequeues 、Forget。

Kubernetes 這邊做的鬆偶做得很不錯, 有新的計算延遲的方法 rateLimitingType 根本不用動,透過 rateLimitingType 代理呼叫 RateLimiter 就好,更上層的使用者也只會使用 RateLimitingInterface 而已。

new function

source code

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
// NewRateLimitingQueue constructs a new workqueue with rateLimited queuing ability
// Remember to call Forget!  If you don't, you may end up tracking failures forever.
//使用者在使用RateLimiting的時候可以傳入自己實作的RateLimiter,此時使用預設的delay work queue。
func NewRateLimitingQueue(rateLimiter RateLimiter) RateLimitingInterface {
	return &rateLimitingType{
		DelayingInterface: NewDelayingQueue(),        //前一小節有提到過delating work queue的newfunction
		rateLimiter:       rateLimiter,               //自行實作的rateLimiter
	}
}


//使用者在使用RateLimiting的時候可以傳入自己實作的RateLimiter,此時使用預設的delaying  work queue並且可以設定delaying work queue的metric name。
func NewNamedRateLimitingQueue(rateLimiter RateLimiter, name string) RateLimitingInterface {
	return &rateLimitingType{
		DelayingInterface: NewNamedDelayingQueue(name),    //前一小節有提到delating work queue的newfunction(可以設定metric name)
		rateLimiter:       rateLimiter,                    //自行實作的rateLimiter
	}
}

下一章節會講解實作rateLimiter介面的物件Item Exponential Failure RateLimiter,我們來看看 kubernetes 怎麼限制物件進入 queue 的速度!


Meng Ze Li
WRITTEN BY
Meng Ze Li
Kubernetes / DevOps / Backend

What's on this Page