自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

從 ListWatch 到 WatchList

云計算 云原生
新版中已經(jīng)修復(fù)了 List Stale Read 的問題,對于前兩種情況,其會先從 kube-apiserver 獲取 Etcd 最新的 RV,等待 WatchCache Store 內(nèi)容追平 RV 后再一次性的返回。

分析

可以先設(shè)想一下如果自己去實現(xiàn)的話,該如何設(shè)計。Client 和 Server 端都要去適配這是必然的,因為 Informer 現(xiàn)在是 ListWatch 機制,服務(wù)端并不支持流式 List。因此可以有個初步的方向:

  1. Server 端支持流式 List 請求
  2. Informer 適配 Server 端 API 的變化

客戶端的適配相對簡單,重點還是放在 Server 端如何實現(xiàn)。先回顧下之前 List 的邏輯,在前一篇 Stale Read 里面已經(jīng)介紹過了。

為方面描述,下文統(tǒng)一使用 RV 代指 Resourceversion,本節(jié)邏輯均基于 v1.26.9 版本,且忽略分頁查詢,因為分頁是直接走 Etcd 的。

無論是 List 還是 Watch 請求,其 query 均支持傳入 RV,服務(wù)端會根據(jù)請求的 RV 的不同做相應(yīng)的處理,根據(jù) RV 的值可以分為三種情況

  1. 未設(shè)置或者顯示設(shè)置 RV=""
  2. RV = "0"
  3. RV = "非 0 值"

對于前兩種情況,List 會直接返回 WatchCache Store 中的內(nèi)容,即服務(wù)端緩存好的 Etcd 的全部相關(guān)數(shù)據(jù)。

對于第三種情況,會等待服務(wù)端緩存數(shù)據(jù)的最大版本要超過傳入的 RV 之后再返回緩存內(nèi)的數(shù)據(jù),如果等待了一段時間(3s)后緩存中的數(shù)據(jù)仍然沒有達到指定版本,則會報錯返回 "Too large resource version",并告訴客戶端可以在 1s 之后重試。

新版中已經(jīng)修復(fù)了 List Stale Read 的問題,對于前兩種情況,其會先從 kube-apiserver 獲取 Etcd 最新的 RV,等待 WatchCache Store 內(nèi)容追平 RV 后再一次性的返回。

也就是說服務(wù)端是可以知道自己是否已經(jīng)包含最新全量數(shù)據(jù)的,在這個基礎(chǔ)上再以流式方式返回即可。當(dāng)前已有的流式 API 就是 Watch,所以可以在此基礎(chǔ)上支持 List 的效果。為什么不直接在 List 請求基礎(chǔ)上改呢,因為改 List 的話,會涉及到太多的客戶端側(cè)的適配,List 會經(jīng)常單獨使用,而 Watch 基本是在 Informer 里面使用。

所以最終的工作就會變成如何使用 Watch API 實現(xiàn) List 的效果,但數(shù)據(jù)仍然以流式返回給客戶端,同時 Informer 修改 ListWatch 方式為只使用 Watch API 實現(xiàn)之前的效果。下文以詳細介紹服務(wù)端實現(xiàn)為主,客戶端適配的部分會比較簡單的介紹下。

原理

通過為 Watch API 添加一個 SendInitialEvents=true 參數(shù)來支持 List 的效果。Server 端接收到 Watch 請求后判斷哪些數(shù)據(jù)是應(yīng)該作為 InitEvents 發(fā)送給客戶端,同時在發(fā)送完這些數(shù)據(jù)之后發(fā)送一個特定的 BOOKMARK Event(帶特定 Annotation 的 BOOKMARK,其 RV 對應(yīng)下文的 bookmarkAfterRV)給客戶端作為服務(wù)端通知客戶端 InitEvents 發(fā)送完畢的標(biāo)志,客戶端在接收到指定 BOOKMARK Event 后,將之前接收到的所有 InitEvent 作為 List 的結(jié)果處理。

時序圖

下面是基于 v1.29 代碼的分析,此時 v1.29 還在 alpha 狀態(tài),提到的舊版代表 1.27 之前的版本,新版代表 v1.29。如果你看到的代碼和下面描述的不一致,有可能是代碼版本導(dǎo)致的。

圖片圖片

從 WatchCache 開始右面四個藍色的是在 kube-apiserver 啟動的時候開始執(zhí)行的,G1 G2 代表兩個 goroutine,分別用來從 Etcd 獲取數(shù)據(jù),以及發(fā)送數(shù)據(jù)給客戶端 CacheWatcher 的 input chan

  1. G1.1 每種資源類型對應(yīng)一個 Cacher,內(nèi)部包含一個 Reflector,WatchCache 作為 Reflector 的 Store 存儲從 Etcd 獲取到的數(shù)據(jù);
  2. G1.2 Reflector 開啟調(diào)用 Etcd List 和 Watch API 獲取數(shù)據(jù);
  3. G1.3 Reflector 利用獲取到的數(shù)據(jù)更新 WatchCache 的 store 和 cyclic buffer,兩者分別用來存儲全量的對象和對象的最近更新事件;
  4. G1.4 在更新完 WatchCache 后,會把 Event 發(fā)送到 Cacher 的 incoming chan 中;
  5. G2.1 從 Cacher 的 imcomming chan 中消費數(shù)據(jù)發(fā)送給所有的 CacheWatcher 的 input chan,或者定時(1 ~ 1.25s)發(fā)送 RV > bookmarkAfterRV 的 BOOKMARK 事件給所有的 CacheWatcher 的 input chan;

上述過程描述了服務(wù)端啟動時的數(shù)據(jù)處理流程,接下來看有客戶端請求時的處理流程

  1. Reflector 首次發(fā)起 Watch 請求,query 中指定 RV=""&sendInitialEvent=true&resourceVersinotallow=NotOlderThan&AllowWatchBookmarks=true,這里無論 RV="" 還是 RV="0" 都可以實現(xiàn) List 的效果,只不過相比舊版本的實現(xiàn),新版里面 Watch 請求針對 RV="" 做了特殊處理,解決了 Watch API Stale Read 的問題(List Stale Read 已經(jīng)在前一篇中介紹過了,針對 List 提供了 FeatureGate 來控制是否開啟 Consistent Read,但 Watch 這里并沒有對應(yīng)的 FeatureGate,也即是說新版中針對 RV="" 的請求一定是 Consistent Read),服務(wù)端接收到請求后為這個請求創(chuàng)建對應(yīng)的 CacheWachter 對象;
  2. Server 端在接收到請求后計算 bookmarkAfterRV 的值,如果 RV="0",則 bookmarkAfterRV 就是 WatchCache RV(WatchCache Store 數(shù)據(jù)中的最大 RV),如果 RV="",則去 Etcd 中獲取最大的 RV 作為 bookmarkAfterRV,將 bookmarkAfterRV 傳遞給 CacheWatcher,最后 CacheWatcher 會結(jié)合 WatchCache Store 和自身 input chan 中的數(shù)據(jù)準(zhǔn)備 InitEvents

2a 開始從 WatchCache Store 中獲取需要返回的數(shù)據(jù),此時的處理邏輯舊版本相同,返回 Store 中的全部數(shù)據(jù),并記錄 Store 數(shù)據(jù)的最大 RV 供下一步使用;

2b 消費 input chan 中的事件,對比其 RV 是否比 2a 傳入的 RV 大,或者如果是 BOOKMARK 類型并且 RV 等與 2a 傳入的 RV,且尚未發(fā)送 bookmarkAfterRV 的事件,則此 BOOKMARK 事件就會被當(dāng)做 List 結(jié)束的標(biāo)志,為其設(shè)置 Annotation: k8s.io/initial-events-end,最后發(fā)送給客戶端;

至此,服務(wù)端的主要流程已經(jīng)介紹完,客戶端 Informer 也做了對應(yīng)的適配,如果開啟 WathList 功能的話,會發(fā)送 Watch 請求來獲取一遍全量數(shù)據(jù),等到接收到攜帶 Annotation: k8s.io/initial-events-end 的 BOOKMARK 事件后,記錄其 RV,將在此期間接受并處理后的對象作為 List 的結(jié)果。最后再次以上述 RV 作為參數(shù)調(diào)用 Watch 請求,從這一步開始就是 Informer 傳統(tǒng)意義上的 Watch 邏輯了。

數(shù)據(jù)流

圖片圖片

圖片來自 KEP 3157 watch-list,其實里面也包含時序圖,不過里面的書序圖畫的有一些問題,和代碼不一致,所以這里并沒有直接使用他的時序圖,而是重新畫了。

可以結(jié)合上面兩個圖理解整個過程,上圖中的 a 對應(yīng)時序圖中的 2a,b 對應(yīng)時序圖中的 2b,c 對應(yīng)時序圖中的 G2.1。最下面白色部分對應(yīng)時序圖中 G1 的邏輯,即從 Etcd 獲取數(shù)據(jù),客戶端請求的處理是自上到下的,而數(shù)據(jù)返回是自下而上的。

注意

上述處理邏輯中存在很多的細節(jié),需要額外注意下

  1. 為 Watch API 修復(fù)了 Stale Read 的問題(RV="" WatchList 功能),本質(zhì)上也是消除 List 的 Stale Read,只不過是在 Watch API 中實現(xiàn)的,這樣結(jié)合上一篇,不管是直接使用 List API 還是使用 WatchList 都能避免 Stale Read 的問題;
  2. WatchCache Store 中的數(shù)據(jù)和 Cacher imcomming chan 數(shù)據(jù)是有交叉的,所以在 2a 處理完所有 Store 數(shù)據(jù)后記錄了最大的 RV 傳遞給 2b 在處理 imcomming chan 的數(shù)據(jù)時使用,event RV > RV 的非 BOOKMARK 事件才會發(fā)回客戶端,這樣是為了避免時間回流;
  3. CacheWatcher 的 input chan 中是不存在 RV < bookmarkAfterRV 的事件的,在 G2.1 從 Cacher incoming chan 消費并發(fā)往所有 CacheWatcher input 的時候判斷了如果事件類型是 BOOKMARK 且 RV < bookmarkAfterRV,則直接丟棄此事件,因為 input chan 緩沖區(qū)大小有限,在其創(chuàng)建后 Cacher 就開始往其 input 寫數(shù)據(jù),而開始消費 input chan 是在 2a 處理完所有 Store 中的數(shù)據(jù)之后,中間存在一段時間差,事件的長短和 Store 中的數(shù)據(jù)量有關(guān)系,丟棄不必要的 BOOKMARK 事件就可以緩解 input chan 的壓力,這里涉及到了為 input chan 添加事件的處理邏輯,里面包括多種特殊情況的處理,例如緩沖滿了如何處理避免因為單個 CacheWatcher 而阻塞整個流程,發(fā)數(shù)據(jù)異常如何處理;
  4. 最終發(fā)回給客戶端的攜帶特定 Annotation 的 BOOKMARK 事件的 RV >= bookmarkAfterRV,這里非常值得注意,并不是等于 bookmarkAfterRV,原 KEP 時序圖中此處(2c)的描述是錯誤的。根本原因在于 bookmark timer 的周期為 1 ~ 1.25s,也就是說每 1 ~ 1.25s 產(chǎn)生一個 BOOKMARK 事件,其 RV 是 incoming chan 最大 RV,正是由于這個時間間隔,結(jié)合 3 的描述,就會導(dǎo)致 G2.1 發(fā)送出去的第一個有效的 (進入到 CacheWatcher input chan) BOOKMARK 事件的 RV >= bookmarkAfterRV。這也從側(cè)面說明了最終在返回 bookmarkAfterRV BOOKMARK 事件之前返回的所有的攜帶有效負載的事件集合的最大 RV 也是 >= bookmarkAfterRV 的,即雖然標(biāo)記是 bookmarkAfterRV,但 List 的結(jié)果中包含比 bookmarkAfterRV 大的數(shù)據(jù)。 個人認為此處還是可以再繼續(xù)優(yōu)化的,可以讓 List 的耗時減少一個 bookmark timer 的周期,即 1 ~ 1.25s,只需要在 2b 處理非 BOOKMARK 事件時判斷 RV == bookmarkAfterRV 且尚未發(fā)送過 bookmarkAfterRV BOOKMARK 事件,此時就可以直接返回一個 bookmarkAfterRV BOOKMARK 給客戶端了,對于數(shù)據(jù)量較大,返回所有數(shù)據(jù)耗時超過 Watch timeout 時間 1s 左右時可以降低超時的概率,避免重復(fù)執(zhí)行 WatchList 的過程,也能在一定程度上降低內(nèi)存消耗。

總結(jié)

本篇主要分析了 WatchList 的實現(xiàn)原理和邏輯,其中不乏一些細節(jié)處理,后續(xù)也會和社區(qū)就有關(guān)細節(jié)進一步討論。在此 KEP 中同時還介紹了另外兩個用來降低 kube-apiserver 內(nèi)存壓力的修改,篇幅有限,將會在下一篇中進行介紹,同時也會給出所有優(yōu)化工作做完前后的效果對比。敬請期待~

責(zé)任編輯:武曉燕 來源: 云原生散修
相關(guān)推薦

2022-07-27 11:10:49

SPserverBRPC引擎

2021-03-03 08:18:54

Service組件

2022-05-09 08:35:43

面試產(chǎn)品互聯(lián)網(wǎng)

2015-09-17 13:09:48

預(yù)裝軟件毒瘤國產(chǎn)手機

2013-06-06 13:42:48

OSPF入門配置

2023-10-12 15:38:50

FreeDOS命令

2017-06-26 09:15:39

SQL數(shù)據(jù)庫基礎(chǔ)

2013-04-07 10:10:23

2022-09-04 21:46:12

數(shù)據(jù)信息風(fēng)險

2025-02-05 09:55:29

2010-02-06 15:31:18

ibmdwAndroid

2009-07-22 14:55:16

ibmdwAndroid

2023-12-27 06:48:49

KubernetesDevOpsHTTP

2021-02-21 22:53:01

CanvasHTML5JavaScript

2020-10-27 06:39:14

智慧城市智慧旅游物聯(lián)網(wǎng)

2016-11-28 16:23:23

戴爾

2010-12-22 12:00:48

軟件保護軟件授權(quán)

2019-07-02 14:17:18

API網(wǎng)關(guān)網(wǎng)關(guān)流量

2018-01-18 04:52:07

點贊
收藏

51CTO技術(shù)棧公眾號