etcd 笔记(07)— 键值对读写操作过程
1. 讀寫總體概述
etcd 各個(gè)模塊交互的總覽,如下圖所示:
總體上的請求流程從上至下依次為客戶端 → API 接口層 → etcd Server → etcd raft 算法庫。
- 讀請求
客戶端通過負(fù)載均衡選擇一個(gè) etcd 節(jié)點(diǎn)發(fā)出讀請求,API 接口層提供了 Range RPC 方法,etcd 服務(wù)端攔截到 gRPC 讀請求后,調(diào)用相應(yīng)的處理器處理請求。
- 寫請求
客戶端通過負(fù)載均衡選擇一個(gè) etcd 節(jié)點(diǎn)發(fā)起寫請求,etcd 服務(wù)端攔截到 gRPC 寫請求,涉及一些校驗(yàn)和監(jiān)控,之后 KVServer 向 raft 模塊發(fā)起提案,內(nèi)容即為寫入數(shù)據(jù)的命令。經(jīng)過網(wǎng)絡(luò)轉(zhuǎn)發(fā),當(dāng)集群中的多數(shù)節(jié)點(diǎn)達(dá)成一致并持久化數(shù)據(jù)后,狀態(tài)變更且 MVCC 模塊執(zhí)行提案內(nèi)容。
2. 讀操作整體流程
將整個(gè)讀操作劃分成如下幾個(gè)步驟:
-
etcdctl會創(chuàng)建一個(gè)clientv3庫對象,選取一個(gè)合適的etcd節(jié)點(diǎn); -
調(diào)用
KVServer模塊的Range RPC方法,發(fā)送請求; -
攔截器攔截,主要做一些校驗(yàn)和監(jiān)控;
-
調(diào)用
KVServer模塊的Range接口獲取數(shù)據(jù);
接著就進(jìn)入了讀請求的核心步驟,會經(jīng)過線性讀 ReadIndex 模塊、MVCC(包含 treeIndex 和 BlotDB)模塊。
線性讀是相對串行讀來講的概念。集群模式下會有多個(gè)
etcd節(jié)點(diǎn),不同節(jié)點(diǎn)之間可能存在一致性問題,串行讀直接返回狀態(tài)數(shù)據(jù),不需要與集群中其他節(jié)點(diǎn)交互。這種方式速度快,開銷小,但是會存在數(shù)據(jù)不一致的情況。
線性讀則需要集群成員之間達(dá)成共識,存在開銷,響應(yīng)速度相對慢。但是能夠保證數(shù)據(jù)的一致性,etcd 默認(rèn)讀模式是線性讀。
繼續(xù)往下,看看如何讀取 etcd 中的數(shù)據(jù)。etcd 中查詢請求,查詢單個(gè)鍵或者一組鍵,以及查詢數(shù)量,到了底層實(shí)際都會調(diào)用 Range keys 方法。
Range 請求的結(jié)構(gòu)圖如下所示:
從上至下,查詢鍵值對的流程包括:
-
在
treeIndex中根據(jù)鍵利用BTree快速查詢該鍵對應(yīng)的索引項(xiàng)keyIndex,索引項(xiàng)中包含Revision; -
根據(jù)查詢到的版本號信息
Revision,在Backend的緩存Buffer中利用二分法查找,如果命中則直接返回; -
若緩存中不符合條件,在
BlotDB中查找(基于BlotDB的索引),查詢之后返回鍵值對信息。
圖中 ReadTx 和 BatchTx 是兩個(gè)接口,用于讀寫請求。在創(chuàng)建 Backend 結(jié)構(gòu)體時(shí),默認(rèn)也會創(chuàng)建 readTx 和 batchTx,readTx 實(shí)現(xiàn)了 ReadTx ,負(fù)責(zé)處理只讀請求;batchTx 實(shí)現(xiàn)了 BatchTx 接口,負(fù)責(zé)處理讀寫請求。
總結(jié)客戶端發(fā)起讀請求之后的處理流程,如下圖所示:
-
客戶端發(fā)起請求之后,
clientv3首先會根據(jù)負(fù)載均衡算法選擇一個(gè)合適的etcd節(jié)點(diǎn),接著調(diào)用KVServer模塊對應(yīng)的RPC接口,發(fā)起Range請求的gRPC遠(yuǎn)程調(diào)用; -
gRPC Server上注冊的攔截器攔截到Range請求,實(shí)現(xiàn)Metrics統(tǒng)計(jì)、日志記錄等功能; -
然后進(jìn)入讀的主要過程,
etcd模式實(shí)現(xiàn)了線性讀,使得任何客戶端通過線性讀都能及時(shí)訪問到鍵值對的更新; -
線性讀獲取到
Leader已提交日志索引構(gòu)造的最新ReadState對象,實(shí)現(xiàn)本節(jié)點(diǎn)狀態(tài)機(jī)的同步; -
接著就是調(diào)用
MVCC模塊,根據(jù)treeIndex模塊B-tree快速查找key對應(yīng)的版本號; -
通過獲取的版本號作為
key,查詢存儲在boltdb中的鍵值對;
3. 寫操作整體流程
將整個(gè)寫操作劃分成如下幾個(gè)步驟:
-
客戶端通過負(fù)載均衡算法選擇一個(gè)
etcd節(jié)點(diǎn),發(fā)起gRPC調(diào)用; -
etcd Server收到客戶端請求; -
經(jīng)過
gRPC攔截、Quota校驗(yàn),Quota模塊用于校驗(yàn)etcd db文件大小是否超過了配額; -
接著
KVServer模塊將請求發(fā)送給本模塊中的raft,這里負(fù)責(zé)與etcd raft模塊進(jìn)行通信,發(fā)起一個(gè)提案,命令為put foo bar,即使用put方法將foo更新為bar; -
提案經(jīng)過轉(zhuǎn)發(fā)之后,半數(shù)節(jié)點(diǎn)成功持久化;
-
MVCC模塊更新狀態(tài)機(jī);
put 接口的執(zhí)行過程:
調(diào)用 put 向 etcd 寫入數(shù)據(jù)時(shí),首先會使用傳入的鍵構(gòu)建 keyIndex 結(jié)構(gòu)體,基于 currentRevision 自增生成新的 Revision 如 {1,0} ,并從 treeIndex 中獲取相關(guān)版本 Revision 等信息;寫事務(wù)提交之后,將本次寫操作的緩存 buffer 合并(merge)到讀緩存上(圖中 ReadTx 中的緩存)。
revision{1,0} 是生成的全局版本號,作為 BoltDB 的 key,經(jīng)過序列化包括 key 名稱、key 創(chuàng)建時(shí)的版本號(create_revision)、value 值和租約等信息為二進(jìn)制數(shù)據(jù)之后,將填充到 BoltDB 的 value 中,同時(shí)將該鍵和 Revision 等信息存儲到 Btree。
根據(jù) etcd 讀寫流程圖,可以知道讀寫操作依賴 MVCC 模塊的 treeIndex 和 BoltDB,treeIndex 用來保存鍵的歷史版本號信息,而 BoltDB 用來保存 etcd 的鍵值對數(shù)據(jù)。通過這兩個(gè)模塊之間的協(xié)作,實(shí)現(xiàn)了 etcd 數(shù)據(jù)的讀取和存儲。
寫請求的處理流程,如下圖所示:
- 客戶端發(fā)送寫請求,通過負(fù)載均衡算法選取合適的 etcd 節(jié)點(diǎn),發(fā)起 gRPC 調(diào)用。
- etcd server 的 gRPC Server 收到這個(gè)請求,經(jīng)過 gRPC 攔截器攔截,實(shí)現(xiàn) Metrics 統(tǒng)計(jì)和日志記錄等功能。
- Quota 模塊配額檢查 db 的大小,如果超過會報(bào)etcdserver: mvcc: database space exceeded的告警,通過 Raft 日志同步給集群中的節(jié)點(diǎn) db 空間不足,同時(shí)告警也會持久化到 db 中。etcd 服務(wù)端拒絕寫入,對外提供只讀的功能。
- 配額檢查通過,KVServer 模塊經(jīng)過限速、鑒權(quán)、包大小判斷之后,生成唯一的編號,這時(shí)才會將寫請求封裝為提案消息,提交給 Raft 模塊。
- 寫請求的提案只能由 Leader 處理,獲取到 Raft 模塊的日志條目之后,Leader 會廣播提案內(nèi)容。WAL 模塊完成 Raft 日志條目內(nèi)容封裝,當(dāng)集群大多數(shù)節(jié)點(diǎn)完成日志條目的持久化,即將提案的狀態(tài)變更為已提交,可以執(zhí)行提案內(nèi)容。
- Apply 模塊用于執(zhí)行提案,首先會判斷該提案是否被執(zhí)行過,如果已經(jīng)執(zhí)行,則直接返回結(jié)束;未執(zhí)行過的情況下,將會進(jìn)入 MVCC 模塊執(zhí)行持久化提案內(nèi)容的操作。
- MVCC 模塊中的 treeIndex 保存了 key 的歷史版本號信息,treeIndex 使用 B-tree 結(jié)構(gòu)維護(hù)了 key 對應(yīng)的版本信息,包含了全局版本號、修改次數(shù)等屬性。版本號代表著 etcd 中的邏輯時(shí)鐘,啟動(dòng)時(shí)默認(rèn)的版本號為 1。鍵值對的修改、寫入和刪除都會使得版本號全局單調(diào)遞增。寫事務(wù)在執(zhí)行時(shí),首先根據(jù)寫入的 key 獲取或者更新索引,如果不存在該 key,則會給予當(dāng)前最大的 currentRevision 自增得到 revision;否則直接根據(jù) key 獲取 revision。
- 根據(jù)從 treeIndex 中獲取到 revision 、修改次數(shù)等屬性,以及 put 請求傳遞的 key-value 信息,作為寫入到 boltdb 的 value,而將 revision 作為寫入到 boltdb 的 key。同時(shí)為了讀請求能夠獲取最新的數(shù)據(jù),etcd 在寫入 boltdb 時(shí)也會同步數(shù)據(jù)到 buffer。因此上文介紹 etcd 讀請求的過程時(shí),會優(yōu)先從 buffer 中讀取,讀取不到的情況下才會從 boltdb 讀取,以此來保證一致性和性能。為了提高吞吐量,此時(shí)提案數(shù)據(jù)并未提交保存到 db 文件,而是由 backend 異步 goroutine 定時(shí)將批量事務(wù)提交。
- Server 通過調(diào)用網(wǎng)絡(luò)層接口返回結(jié)果給客戶端。
總的來說,這個(gè)過程為客戶端發(fā)起寫請求,由 Leader 節(jié)點(diǎn)處理,經(jīng)過攔截器、Quota 配額檢查之后,KVServer 提交一個(gè)寫請求的提案給 Raft 一致性模塊,經(jīng)過 RaftHTTP 網(wǎng)絡(luò)轉(zhuǎn)發(fā),集群中的其他節(jié)點(diǎn)半數(shù)以上持久化成功日志條目,提案的狀態(tài)將會變成已提交。接著 Apply 通過 MVCC 的 treeIndex、boltdb 執(zhí)行提案內(nèi)容,成功之后更新狀態(tài)機(jī)。
原文:
https://kaiwu.lagou.com/course/courseInfo.htm?courseId=613#/detail/pc?id=6411
總結(jié)
以上是生活随笔為你收集整理的etcd 笔记(07)— 键值对读写操作过程的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 2022-2028年中国汽车橡胶件行业市
- 下一篇: 2022-2028年中国打印耗材市场现状