前言
本文聚焦client-go v11.0.0 controller框架DeltaFIFO對象,分析源碼理解DeltaFIFO的實現(xiàn)。DeltaFIFO是client-go controller framework的重要環(huán)節(jié),它的作用是保證Reflector和Indexer之間對象同步??梢哉f,DeltaFIFO是連接生產者(Reflector)和消費者(Indexer)的通道。閱讀本文前希望讀者已閱讀《client-go源碼分析》前面的系列文章,要求已了解Informer和Reflector。
1 DeltaFIFO介紹

以簡單的示意圖描述DeltaFIFO的結構:

DeltaFIFO的核心結構包括items和queue,對應Delta和FIFO。
1.1 Delta
Delta顧名思義是變化和差異,因此items的作用是緩存某個對象的一系列變更行為(Deltas)。正因為這里保存的是Delta(當前對象和前一個對象的差異),有序地保存一系列行為才有意義,單獨的某個Updated狀態(tài)無法正確同步對象。
items是map結構,key是由keyfunc計算出來的對象key;value是deltas,即delta列表。delta數(shù)據(jù)結構如下:

當前版本DeltaType的值只有Added, Updated, Deleted, Sync;Object即runtime.Object。
1.2 FIFO
FIFO即先進先出隊列,這里queue僅把對象鍵(objkey)入列。消費者通過objkey查找items的Deltas并依次同步所有行為。queue是對象同步處理隊列,消費者僅處理queue中存在的objkey。
2 DeltaFIFO基本功能
上述已經提到DeltaType只有四種:Added, Updated, Deleted, Sync。這里僅分析最難理解的Sync函數(shù),Sync是List機制和Resync觸發(fā)的全量同步;Added,Updated,Deleted是Watch觸發(fā)的增量同步。
2.1 深入理解Sync:Replace()
Sync是比較特殊的處理,理解了Sync也就不難理解其他行為。Sync是指Reflector全量同步Indexer,從DelaFIFO的實現(xiàn)看是調用Replace函數(shù)“重建”Indexer。DelaFIFO的Replace實現(xiàn)了Sync行為(Resync也調用該函數(shù))。controller第一次啟動時,Reflector的List機制觸發(fā)DeltaFIFO的Sync行為,《Client-Go源碼分析(2.1):Reflector》會提到r.syncWith(items,
resourceVersion)函數(shù),該函數(shù)調用
func (f *DeltaFIFO) Replace(list []interface{},
resourceVersion string) error
開始分析Replace代碼前,先看一個重要的公共函數(shù)queueActionLocked,DeltaFIFO的所有行為都會調用它。queueActionLocked的功能是:僅處理某個動作。若處理一系列動作或多個對象,需要外部調用者重復調用它。




2.2 queueActionLocked的特殊處理分析
上文提到的queueActionLocked特殊處理針對一種場景:
obj的id是objkey1,該對象最后一次動作是Deleted,并且objkey1仍在queue中等待處理。此時周期性被動觸發(fā)一次Resync(會調用Replace)。k8s集群真實的runtime.Object已刪除,但Indexer的同步尚未完成,Indexer緩存里仍有這個對象。

假設queueActionLocked更新了deltas如上圖(左)所示,該對象會先刪除,然后添加回來!從Pop()函數(shù)里的回調函數(shù)Process可以看到這個過程。前幾篇文章已經提到過,Process的入?yún)bj是Deltas隊列,該函數(shù)依次處理每個Delta行為。當Process處理到上圖(左)Deleted時Index會刪除對象;當它處理到最后的Sync時,Index又會添加這個對象。因此,queueActionLocked通過特殊處理規(guī)避了這個問題,現(xiàn)有代碼的處理方式如上圖(右)所示,直接返回不更新deltas。

2.3 sync場景的DeleteFinalStateUnknown分析
// DeletedFinalStateUnknown isplaced into a DeltaFIFO in the case where
// an object was deleted but thewatch deletion event was missed. In this
// case we don't know the final"resting" state of the object, so there's
// a chance the included `Obj` isstale.
這個對象困惑了我許久,從注釋上看是針對Watch Deleted事件丟失,導致k8s實際對象和Indexer緩存不一致的場景。我們用圖展示兩種場景:1. Deleted事件未丟失,只是queue待處理該對象;2. Deleted事件丟失。

上圖(左)是Deleted消息丟失的代碼處理,queueActionLocked會添加DeleteFinalStateUnknown對象到deltas,并保存到Indexer。DeleteFinalStateUnknown是對runtime.Object的封裝。
上圖(右)是Deleted消息未丟失的代碼處理。deltas對象變化過程如下:

從Process回調函數(shù)看,是直接調用clientState.Delete(d.Object)刪除對象的,Indexer計算對象鍵是否會異常呢?抱著好奇心看了一眼keyFunc(計算對象鍵的函數(shù),下一篇文章會講到)DeletionHandlingMetaNamespaceKeyFunc:

DeletionHandlingMetaNamespaceKeyFunc果然有對DeletedFinalStateUnknown的處理(obj.(DeletedFinalStateUnknown)是類型斷言用法),因此不需要擔心獲取對象鍵會異常。如果是我們自己實現(xiàn)keyFunc,一樣要考慮到DeletedFinalStateUnknown這個而特殊對象。
到此為止DeltaFIFO的主要機制已分析完畢。
3 結語
DeltaFIFO的部分代碼比較晦澀,尤其是特殊處理增加了閱讀難度。特殊處理一時爽,代碼維護兩行淚。
4 參考文檔