背景
什么是 Informer 機(jī)制
一個(gè)控制器每次需要獲取對(duì)象的時(shí)候都要訪問(wèn) APIServer置森,這會(huì)給系統(tǒng)帶來(lái)很高的負(fù)載,Informers 的內(nèi)存緩存就是來(lái)解決這個(gè)問(wèn)題的塞琼,此外 Informers 還可以幾乎實(shí)時(shí)的監(jiān)控對(duì)象的變化,而不需要輪詢請(qǐng)求禁舷,這樣就可以保證客戶端的緩存數(shù)據(jù)和服務(wù)端的數(shù)據(jù)一致彪杉,就可以大大降低 APIServer 的壓力了。
如上圖展示了 Informer 的基本處理流程:
以 events 事件的方式從 APIServer 獲取數(shù)據(jù)
提供一個(gè)類似客戶端的 Lister 接口牵咙,從內(nèi)存緩存中 get 和 list 對(duì)象
為添加派近、刪除、更新注冊(cè)事件處理程序
此外 Informers 也有錯(cuò)誤處理方式洁桌,當(dāng)長(zhǎng)期運(yùn)行的 watch 連接中斷時(shí)渴丸,它們會(huì)嘗試使用另一個(gè) watch 請(qǐng)求來(lái)恢復(fù)連接,在不丟失任何事件的情況下恢復(fù)事件流。如果中斷的時(shí)間較長(zhǎng)谱轨,而且 APIServer 丟失了事件(etcd 在新的 watch 請(qǐng)求成功之前從數(shù)據(jù)庫(kù)中清除了這些事件)戒幔,那么 Informers 就會(huì)重新 List 全量數(shù)據(jù)。
而且在重新 List 全量操作的時(shí)候還可以配置一個(gè)重新同步的周期參數(shù)土童,用于協(xié)調(diào)內(nèi)存緩存數(shù)據(jù)和業(yè)務(wù)邏輯的數(shù)據(jù)一致性诗茎,每次過(guò)了該周期后,注冊(cè)的事件處理程序就將被所有的對(duì)象調(diào)用献汗,通常這個(gè)周期參數(shù)以分為單位敢订,比如10分鐘或者30分鐘。
Informers 的這些高級(jí)特性以及超強(qiáng)的魯棒性罢吃,都足以讓我們不去直接使用客戶端的 Watch() 方法來(lái)處理自己的業(yè)務(wù)邏輯楚午,而且在 Kubernetes 中也有很多地方都有使用到 Informers。但是在使用 Informers 的時(shí)候尿招,通常每個(gè) GroupVersionResource(GVR)只實(shí)例化一個(gè) Informers醒叁,但是有時(shí)候我們?cè)谝粋€(gè)應(yīng)用中往往有使用多種資源對(duì)象的需求,這個(gè)時(shí)候?yàn)榱朔奖愎蚕?Informers泊业,我們可以通過(guò)使用共享 Informer 工廠來(lái)實(shí)例化一個(gè) Informer把沼。
共享 Informer 工廠允許我們?cè)趹?yīng)用中為同一個(gè)資源共享 Informer,也就是說(shuō)不同的控制器循環(huán)可以使用相同的 watch 連接到后臺(tái)的 APIServer吁伺,例如饮睬,kube-controller-manager 中的控制器數(shù)據(jù)量就非常多,但是對(duì)于每個(gè)資源(比如 Pod)篮奄,在這個(gè)進(jìn)程中只有一個(gè) Informer捆愁。
Informer 是 client-go 中的核心工具包,已經(jīng)被 kubernetes 中眾多組件所使用窟却。所謂 Informer昼丑,其實(shí)就是一個(gè)帶有本地緩存和索引機(jī)制的、可以注冊(cè) EventHandler 的 client夸赫,本地緩存被稱為 Store菩帝,索引被稱為 Index。使用 informer 的目的是為了減輕 apiserver 數(shù)據(jù)交互的壓力而抽象出來(lái)的一個(gè) cache 層, 客戶端對(duì) apiserver 數(shù)據(jù)的 "讀取" 和 "監(jiān)聽" 操作都通過(guò)本地 informer 進(jìn)行茬腿。
Informer 的主要功能:
同步數(shù)據(jù)到本地緩存
根據(jù)對(duì)應(yīng)的事件類型呼奢,觸發(fā)事先注冊(cè)好的 ResourceEventHandle
為什么需要 Informer 機(jī)制?
我們知道Kubernetes各個(gè)組件都是通過(guò)REST API跟API Server交互通信的切平,而如果每次每一個(gè)組件都直接跟API Server交互去讀取/寫入到后端的etcd的話握础,會(huì)對(duì)API Server以及etcd造成非常大的負(fù)擔(dān)。 而Informer機(jī)制是為了保證各個(gè)組件之間通信的實(shí)時(shí)性悴品、可靠性禀综,并且減緩對(duì)API Server和etcd的負(fù)擔(dān)简烘。
Informer 需要滿足哪些要求?
消息可靠性
消息實(shí)時(shí)性
消息順序性
高性能
核心功能
Informer的工作流程
Informer 首先會(huì) list/watch apiserver定枷,Informer 所使用的 Reflector 包負(fù)責(zé)與 apiserver 建立連接孤澎,Reflector 使用 ListAndWatch 的方法,會(huì)先從 apiserver 中 list 該資源的所有實(shí)例依鸥,list 會(huì)拿到該對(duì)象最新的 resourceVersion,然后使用 watch 方法監(jiān)聽該 resourceVersion 之后的所有變化悼沈,若中途出現(xiàn)異常贱迟,reflector 則會(huì)從斷開的 resourceVersion 處重現(xiàn)嘗試監(jiān)聽所有變化,一旦該對(duì)象的實(shí)例有創(chuàng)建絮供、刪除衣吠、更新動(dòng)作,Reflector 都會(huì)收到"事件通知"壤靶,這時(shí)缚俏,該事件及它對(duì)應(yīng)的 API 對(duì)象這個(gè)組合,被稱為增量(Delta)贮乳,它會(huì)被放進(jìn) DeltaFIFO 中忧换。
Informer 會(huì)不斷地從這個(gè) DeltaFIFO 中讀取增量,每拿出一個(gè)對(duì)象向拆,Informer 就會(huì)判斷這個(gè)增量的時(shí)間類型亚茬,然后創(chuàng)建或更新本地的緩存,也就是 store浓恳。
如果事件類型是 Added(添加對(duì)象)刹缝,那么 Informer 會(huì)通過(guò) Indexer 的庫(kù)把這個(gè)增量里的 API 對(duì)象保存到本地的緩存中,并為它創(chuàng)建索引颈将,若為刪除操作梢夯,則在本地緩存中刪除該對(duì)象。
DeltaFIFO 再 pop 這個(gè)事件到 controller 中晴圾,controller 會(huì)調(diào)用事先注冊(cè)的 ResourceEventHandler 回調(diào)函數(shù)進(jìn)行處理颂砸。
在 ResourceEventHandler 回調(diào)函數(shù)中,其實(shí)只是做了一些很簡(jiǎn)單的過(guò)濾死姚,然后將關(guān)心變更的 Object 放到 workqueue 里面沾凄。
Controller 從 workqueue 里面取出 Object,啟動(dòng)一個(gè) worker 來(lái)執(zhí)行自己的業(yè)務(wù)邏輯知允,業(yè)務(wù)邏輯通常是計(jì)算目前集群的狀態(tài)和用戶希望達(dá)到的狀態(tài)有多大的區(qū)別撒蟀,然后孜孜不倦地讓 apiserver 將狀態(tài)演化到用戶希望達(dá)到的狀態(tài),比如為 deployment 創(chuàng)建新的 pods温鸽,或者是擴(kuò)容/縮容 deployment保屯。
在worker中就可以使用 lister 來(lái)獲取 resource手负,而不用頻繁的訪問(wèn) apiserver,因?yàn)?apiserver 中 resource 的變更都會(huì)反映到本地的 cache 中姑尺。
List & Watch
List所做的竟终,就是向API Server發(fā)送一個(gè)http短鏈接請(qǐng)求,羅列所有目標(biāo)資源的對(duì)象切蟋。而Watch所做的是實(shí)際的“監(jiān)聽”工作统捶,通過(guò)http長(zhǎng)鏈接的方式,其與API Server能夠建立一個(gè)持久的監(jiān)聽關(guān)系柄粹,當(dāng)目標(biāo)資源發(fā)生了變化時(shí)喘鸟,API Server會(huì)返回一個(gè)對(duì)應(yīng)的事件,從而完成一次成功的監(jiān)聽驻右,之后的事情便交給后面的handler來(lái)做什黑。
這樣一個(gè)List & Watch機(jī)制,帶來(lái)了如下幾個(gè)優(yōu)勢(shì):
事件響應(yīng)的實(shí)時(shí)性:通過(guò)Watch的調(diào)用堪夭,當(dāng)API Server中的目標(biāo)資源產(chǎn)生變化時(shí)愕把,能夠及時(shí)的收到事件的返回,從而保證了事件響應(yīng)的實(shí)時(shí)性森爽。而倘若是一個(gè)輪詢的機(jī)制恨豁,其實(shí)時(shí)性將受限于輪詢的時(shí)間間隔。
事件響應(yīng)的可靠性:倘若僅調(diào)用Watch爬迟,則如果在某個(gè)時(shí)間點(diǎn)連接被斷開圣絮,就可能導(dǎo)致事件被丟失。List的調(diào)用帶來(lái)了查詢資源期望狀態(tài)的能力雕旨,客戶端通過(guò)期望狀態(tài)與實(shí)際狀態(tài)的對(duì)比扮匠,可以糾正狀態(tài)的不一致。二者結(jié)合保證了事件響應(yīng)的可靠性凡涩。
高性能:倘若僅周期性地調(diào)用List棒搜,輪詢地獲取資源的期望狀態(tài)并在與當(dāng)前狀態(tài)不一致時(shí)執(zhí)行更新,自然也可以do the job活箕。但是高頻的輪詢會(huì)大大增加API Server的負(fù)擔(dān)力麸,低頻的輪詢也會(huì)影響事件響應(yīng)的實(shí)時(shí)性。Watch這一異步消息機(jī)制的結(jié)合育韩,在保證了實(shí)時(shí)性的基礎(chǔ)上也減少了API Server的負(fù)擔(dān)克蚂,保證了高性能。
事件處理的順序性:我們知道筋讨,每個(gè)資源對(duì)象都有一個(gè)遞增的ResourceVersion埃叭,唯一地標(biāo)識(shí)它當(dāng)前的狀態(tài)是“第幾個(gè)版本”,每當(dāng)這個(gè)資源內(nèi)容發(fā)生變化時(shí)悉罕,對(duì)應(yīng)產(chǎn)生的事件的ResourceVersion也會(huì)相應(yīng)增加赤屋。在并發(fā)場(chǎng)景下立镶,K8s可能獲得同一資源的多個(gè)事件,由于K8s只關(guān)心資源的最終狀態(tài)类早,因此只需要確保執(zhí)行事件的ResourceVersion是最新的媚媒,即可確保事件處理的順序性。
ResourceVersion
Kubernetes 請(qǐng)求并發(fā)控制與數(shù)據(jù)一致性(含ResourceVersion涩僻、Update缭召、Patch簡(jiǎn)析)
Kubernetes-resourceVersion機(jī)制分析
秘訣就是 Chunked transfer encoding(分塊傳輸編碼),它首次出現(xiàn)在HTTP/1.1逆日。正如維基百科所說(shuō):
HTTP 分塊傳輸編碼允許服務(wù)器為動(dòng)態(tài)生成的內(nèi)容維持 HTTP 持久鏈接嵌巷。通常,持久鏈接需要服務(wù)器在開始發(fā)送消息體前發(fā)送Content-Length消息頭字段屏富,但是對(duì)于動(dòng)態(tài)生成的內(nèi)容來(lái)說(shuō)晴竞,在內(nèi)容創(chuàng)建完之前是不可知的蛙卤。使用分塊傳輸編碼狠半,數(shù)據(jù)分解成一系列數(shù)據(jù)塊,并以一個(gè)或多個(gè)塊發(fā)送颤难,這樣服務(wù)器可以發(fā)送數(shù)據(jù)而不需要預(yù)先知道發(fā)送內(nèi)容的總大小神年。
當(dāng)客戶端調(diào)用 watch API 時(shí),apiserver 在response 的 HTTP Header 中設(shè)置 Transfer-Encoding的值為chunked行嗤,表示采用分塊傳輸編碼已日,客戶端收到該信息后,便和服務(wù)端該鏈接栅屏,并等待下一個(gè)數(shù)據(jù)塊飘千,即資源的事件信息。例如:
Informer 能保證通過(guò)list+watch不會(huì)丟失事件栈雳,如果網(wǎng)絡(luò)抖動(dòng)重新恢復(fù)后护奈,watch會(huì)帶著之前的resourceVersion號(hào)重連,resourceVersion是單調(diào)遞增的哥纫, API Server 收到該請(qǐng)求后會(huì)將所有大于該resourceVersion的變更同步過(guò)來(lái)霉旗。
二級(jí)緩存
二級(jí)緩存屬于 Informer 的底層緩存機(jī)制,這兩級(jí)緩存分別是 DeltaFIFO 和 LocalStore蛀骇。這兩級(jí)緩存的用途各不相同厌秒。DeltaFIFO 用來(lái)存儲(chǔ) Watch API 返回的各種事件 ,LocalStore 只會(huì)被 Lister 的 List/Get 方法訪問(wèn) 擅憔。
如果K8s每次想查看資源對(duì)象的狀態(tài)鸵闪,都要經(jīng)歷一遍L(zhǎng)ist調(diào)用,顯然對(duì) API Server 也是一個(gè)不小的負(fù)擔(dān)暑诸,對(duì)此岛马,一個(gè)容易想到的方法是使用一個(gè)cache作保存棉姐,需要獲取資源狀態(tài)時(shí)直接調(diào)cache,當(dāng)事件來(lái)臨時(shí)除了響應(yīng)事件外啦逆,也對(duì)cache進(jìn)行刷新伞矩。
雖然 Informer 和 Kubernetes 之間沒(méi)有 resync 機(jī)制,但 Informer 內(nèi)部的這兩級(jí)緩存之間存在 resync 機(jī)制夏志。
Resync
Resync 機(jī)制會(huì)將 Indexer 的本地緩存重新同步到 DeltaFIFO 隊(duì)列中乃坤。一般我們會(huì)設(shè)置一個(gè)時(shí)間周期,讓 Indexer 周期性地將緩存同步到隊(duì)列中沟蔑。直接 list/watch API Server 就已經(jīng)能拿到集群中資源對(duì)象變化的 event 了湿诊,這里引入 Resync 的作用是什么呢?去掉會(huì)有什么影響呢瘦材?
自定義事件處理
ResourceEventHandler 用于處理對(duì)象的變更事件厅须,用戶可以通過(guò)實(shí)現(xiàn) ResourceEventHandler 接口,并調(diào)用 sharedIndexInformer.AddEventHandler() 或 sharedIndexInformer.AddEventHandlerWithResyncPeriod() 方法注冊(cè)到 sharedProcessor 中食棕。這樣朗和,當(dāng)數(shù)據(jù)發(fā)送變化時(shí),就會(huì)回調(diào) ResourceEventHandler 中對(duì)應(yīng)的 OnAdd/OnUpdate/OnDelete 方法來(lái)實(shí)現(xiàn)用戶自定義的處理邏輯
核心對(duì)象
Informer相關(guān)
client-go 中提供了幾種不同的 Informer:
通過(guò)調(diào)用 NewInformer 函數(shù)創(chuàng)建一個(gè)簡(jiǎn)單的不帶 indexer 的 Informer簿晓。
通過(guò)調(diào)用 NewIndexerInformer 函數(shù)創(chuàng)建一個(gè)簡(jiǎn)單的帶 indexer 的 Informer眶拉。
通過(guò)調(diào)用 NewSharedIndexInformer 函數(shù)創(chuàng)建一個(gè) Shared 的 Informer。
通過(guò)調(diào)用 NewDynamicSharedInformerFactory 函數(shù)創(chuàng)建一個(gè)為 Dynamic 客戶端的 Shared 的 Informer憔儿。
這里帶有 Indexer 和不帶 Indexer 的大家好理解寫忆植,從字面意思來(lái)看,就是一個(gè)是帶有 Indexer 功能一個(gè)不帶有 Indexer 功能的 Informer谒臼。而這里的 Shared 的 Informer 引入朝刊,其實(shí)是因?yàn)殡S著 K8S 中,相同資源的監(jiān)聽者在不斷地增加蜈缤,從而導(dǎo)致很多調(diào)用者通過(guò) Watch API 對(duì) API Server 建立一個(gè)長(zhǎng)連接去監(jiān)聽事件的變化拾氓,這將嚴(yán)重增加了 API Server 的工作負(fù)載,及資源的浪費(fèi)劫樟。
比如在 kube-controller-manager 組件中痪枫,有很多控制管理都需要監(jiān)聽 Pod 資源的變化,如果都獨(dú)立的調(diào)用 Informer 去維護(hù)一個(gè)對(duì) APIServer 的長(zhǎng)連接叠艳,這將導(dǎo)致 kube-controller-manager 中資源的浪費(fèi)及增加了 APIServer 的負(fù)載奶陈,而不同控制管理者通過(guò)創(chuàng)建 Shared 的 Informer 則實(shí)現(xiàn)了這些控制管理者使用同一個(gè) Watch 去和 APIServer 建立長(zhǎng)連接,并在收到事件后附较,分發(fā)給下游的調(diào)用者吃粒。
SharedInformer
我們平時(shí)說(shuō)的 Informer 其實(shí)就是 SharedInformer,它是可以共享使用的拒课。如果同一個(gè)資源的 Informer 被實(shí)例化多次徐勃,那么就會(huì)運(yùn)行多個(gè) ListAndWatch 操作事示,這會(huì)加大 APIServer 的壓力。而 SharedInformer 通過(guò)一個(gè) map 來(lái)讓同一類資源的 Informer 實(shí)現(xiàn)共享一個(gè) Refelctor僻肖,這樣就不會(huì)出現(xiàn)上面這個(gè)問(wèn)題了。
Informer通過(guò)Local Store緩存目標(biāo)資源對(duì)象臀脏,且僅為自己所用劝堪。但是在K8s中,一個(gè)Controller可以關(guān)心不止一種資源揉稚,使得多個(gè)Controller所關(guān)心的資源彼此會(huì)存在交集秒啦。如果幾個(gè)Controller都用自己的Informer來(lái)緩存同一個(gè)目標(biāo)資源,顯然會(huì)導(dǎo)致不小的空間開銷搀玖,因此K8s引入了SharedInformer來(lái)解決這個(gè)問(wèn)題余境。
SharedInformer擁有為多個(gè)Controller提供一個(gè)共享cache的能力,從而避免資源緩存的重復(fù)灌诅、減小空間開銷芳来。除此之外,一個(gè)SharedInformer對(duì)一種資源只建立一個(gè)與API Server的Watch監(jiān)聽延塑,且能夠?qū)⒈O(jiān)聽得到的事件分發(fā)給下游所有感興趣的Controller绣张,這也顯著地減少了API Server的負(fù)載壓力答渔。實(shí)際上关带,K8s中廣泛使用的都是SharedInformer,Informer則出場(chǎng)甚少沼撕。
SharedIndexInformer
SharedIndexInformer 擴(kuò)展了 SharedInformer 接口宋雏,提供了構(gòu)建索引的能力。
SharedIndexInformerFactory
使用sharedInformerFactory可以統(tǒng)一管理控制器中需要的各資源對(duì)象的informer實(shí)例务豺,避免同一個(gè)資源創(chuàng)建多個(gè)實(shí)例
默認(rèn)的 Informer 實(shí)現(xiàn)
Informer 機(jī)制為 K8s 的各種對(duì)象提供了默認(rèn)的 Informer 實(shí)現(xiàn)磨总,可以通過(guò)以下方式快速創(chuàng)建一個(gè) Informer 對(duì)象,并交由 SharedIndexInformerFactory 統(tǒng)一管理笼沥。
SharedInformerFactory.Core().V1().Nodes()
.Core().V1().Pods()
.Apps().V1().Deployments()
.Core().V1().Secrets()
.Batch().V1beta1().CronJobs()
List-Watch相關(guān)
Reflector
Reflector用來(lái)watch特定的k8s API資源蚪燕。具體的實(shí)現(xiàn)是通過(guò)ListAndWatch的方法,watch可以是k8s內(nèi)建的資源或者是自定義的資源奔浅。當(dāng)reflector通過(guò)watch API接收到有關(guān)新資源實(shí)例存在的通知時(shí)馆纳,它使用相應(yīng)的列表API獲取新創(chuàng)建的對(duì)象,并將其放入watchHandler函數(shù)內(nèi)的Delta Fifo隊(duì)列中汹桦。
ListerWatcher
ListerWatcher 是 Informer 機(jī)制中的核心對(duì)象之一鲁驶,其功能是通過(guò) List() 方法從 API Server 中獲取某一類型的全量數(shù)據(jù),再通過(guò) Watch() 方法監(jiān)聽 API Server 中數(shù)據(jù)的增量更新舞骆。
ListerWatcher 繼承自 Lister 和 Watcher 接口钥弯,從而使其既能獲取全量數(shù)據(jù)径荔,又能監(jiān)聽增量數(shù)據(jù)更新。
Lister
Lister 接口用于完成全量數(shù)據(jù)的初始化脆霎。
Watcher
Watcher 接口用于監(jiān)聽數(shù)據(jù)的增量更新总处。
事件隊(duì)列相關(guān)
Store
Store是一個(gè)通用的對(duì)象存儲(chǔ)接口,其中定義了一系列與對(duì)象增刪改查相關(guān)的方法睛蛛。Store 要求對(duì)象有唯一鍵辨泳,鍵的計(jì)算方式由接口實(shí)現(xiàn)類中關(guān)聯(lián)的 KeyFunc 決定的。
Queue
從 Queue 接口的定義可以看出玖院,它繼承自 Store 接口菠红,所以其具備基本的數(shù)據(jù)存取能力。同時(shí)难菌,它又具備從隊(duì)列頭部取出數(shù)據(jù)并調(diào)用 PopProcessFunc 處理頭部數(shù)據(jù)并返回處理結(jié)果的能力试溯。
DeltaFIFO
DeltaFIFO 是一個(gè)生產(chǎn)者-消費(fèi)者的隊(duì)列,生產(chǎn)者是 Reflector郊酒,消費(fèi)者是 Pop 函數(shù)遇绞,從架構(gòu)圖上可以看出 DeltaFIFO 的數(shù)據(jù)來(lái)源為 Reflector,通過(guò) Pop 操作消費(fèi)數(shù)據(jù)燎窘,消費(fèi)的數(shù)據(jù)一方面存儲(chǔ)到 Indexer 中摹闽,另一方面可以通過(guò) Informer 的 handler 進(jìn)行處理,Informer 的 handler 處理的數(shù)據(jù)需要與存儲(chǔ)在 Indexer 中的數(shù)據(jù)匹配褐健。需要注意的是付鹿,Pop 的單位是一個(gè) Deltas,而不是 Delta蚜迅。
Delta
Delta 是 DeltaFIFO 存儲(chǔ)的類型舵匾,它記錄了對(duì)象發(fā)生了什么變化以及變化后對(duì)象的狀態(tài)。如果變更是刪除谁不,它會(huì)記錄對(duì)象刪除之前的最終狀態(tài)坐梯。
Deltas
Deltas 保存了對(duì)象狀態(tài)的變更(Add/Delete/Update)信息(如 Pod 的刪除添加等),Deltas 緩存了針對(duì)相同對(duì)象的多個(gè)狀態(tài)變更信息刹帕,如 Pod 的 Deltas[0]可能更新了標(biāo)簽吵血,Deltas[1]可能刪除了該 Pod。最老的狀態(tài)變更信息為 Oldest()偷溺,最新的狀態(tài)變更信息為 Newest()蹋辅,使用中,獲取 DeltaFIFO 中對(duì)象的 key 以及獲取 DeltaFIFO 都以最新狀態(tài)為準(zhǔn)亡蓉。
最舊的 delta 在索引0位置晕翠,最新的 delta 在最后一個(gè)索引位置。
DeltaType
const (
Added DeltaType = "Added" // 增加
Updated DeltaType = "Updated" // 更新
Deleted DeltaType = "Deleted" // 刪除
Sync DeltaType = "Sync" // 同步
)</pre>
DeltaFIFO 中數(shù)據(jù)存儲(chǔ)形式
事件處理相關(guān)
Controller
Processor
[圖片上傳失敗...(image-25a1a1-1637847474305)]
ResourceEventHandler
當(dāng)經(jīng)過(guò)List & Watch得到事件時(shí),接下來(lái)的實(shí)際響應(yīng)工作就交由ResourceEventHandler來(lái)進(jìn)行淋肾,這個(gè)Interface定義如下:
type ResourceEventHandler interface {
// 添加對(duì)象回調(diào)函數(shù)
OnAdd(obj interface{})
// 更新對(duì)象回調(diào)函數(shù)
OnUpdate(oldObj, newObj interface{})
// 刪除對(duì)象回調(diào)函數(shù)
OnDelete(obj interface{})
}
當(dāng)事件到來(lái)時(shí)硫麻,Informer根據(jù)事件的類型(添加/更新/刪除資源對(duì)象)進(jìn)行判斷,將事件分發(fā)給綁定的EventHandler樊卓,即分別調(diào)用對(duì)應(yīng)的handle方法(OnAdd/OnUpdate/OnDelete)拿愧,最后EventHandler將事件發(fā)送給Workqueue。
緩存相關(guān)
Indexer
Indexer在Store基礎(chǔ)上擴(kuò)展了索引能力碌尔,就好比給數(shù)據(jù)庫(kù)添加的索引浇辜,以便查詢更快,那么肯定需要有個(gè)結(jié)構(gòu)來(lái)保存索引唾戚。典型的索引用例是基于對(duì)象標(biāo)簽創(chuàng)建索引柳洋。 Indexer可以根據(jù)多個(gè)索引函數(shù)維護(hù)索引。Indexer使用線程安全的數(shù)據(jù)存儲(chǔ)來(lái)存儲(chǔ)對(duì)象及其鍵叹坦。 在Store中定義了一個(gè)名為MetaNamespaceKeyFunc 的默認(rèn)函數(shù)熊镣,該函數(shù)生成對(duì)象的鍵作為該對(duì)象的<namespace> / <name>組合。
Reflector 通過(guò) ListAndWatch 把數(shù)據(jù)傳入 DeltaFIFO 后募书,經(jīng)過(guò) DeltaFIFO 的 Pop 函數(shù)將資源對(duì)象存入到了本地的一個(gè)存儲(chǔ) Indexer 中绪囱,而這個(gè)底層真正的存儲(chǔ)其實(shí)就是上面的 ThreadSafeStore。
ThreadSafeStore
使用示例
var kubeconfig *string
if home := homedir.HomeDir(); home != "" {
kubeconfig = flag.String("kubeconfig", filepath.Join(home, ".kube", "config"), "(optional) absolute path to the kubeconfig file")
} else {
kubeconfig = flag.String(, , "absolute path to the kubeconfig file")
}
flag.Parse()
config, err := clientcmd.BuildConfigFromFlags(, *kubeconfig)
if err != nil {
panic(err)
}
// 初始化 client
clientset, err := kubernetes.NewForConfig(config)
if err != nil {
log.Panic(err.Error())
}
stopper := make(chan struct{})
defer close(stopper)
// 初始化 informer
factory := informers.NewSharedInformerFactory(clientset, 0)
nodeInformer := factory.Core().V1().Nodes()
informer := nodeInformer.Informer()
defer runtime.HandleCrash()
// 啟動(dòng) informer莹捡,list & watch
go factory.Start(stopper)
// 從 apiserver 同步資源鬼吵,必不可少
if !cache.WaitForCacheSync(stopper, informer.HasSynced) {
runtime.HandleError(fmt.Errorf("Timed out waiting for caches to sync"))
return
}
// 使用自定義 handler
informer.AddEventHandler(cache.ResourceEventHandlerFuncs{
AddFunc: onAdd,
UpdateFunc: func(interface{}, interface{}) { fmt.Println("update not implemented") }, // 此處省略 workqueue 的使用
DeleteFunc: func(interface{}) { fmt.Println("delete not implemented") },
})