06、etcd 寫請求執行流程

来源:https://www.cnblogs.com/huageyiyangdewo/archive/2023/07/09/17538633.html
-Advertisement-
Play Games

> 本篇內容主要來源於自己學習的視頻,如有侵權,請聯繫刪除,謝謝。 上一節我們學習了 etcd 讀請求執行流程,這一節,我們來學習 etcd 寫請求執行流程。 ### 1、etcd寫請求概覽 **etcd 一個寫請求執行流程又是怎樣的呢?** ``` sh etcdctl put hello wor ...


本篇內容主要來源於自己學習的視頻,如有侵權,請聯繫刪除,謝謝。

上一節我們學習了 etcd 讀請求執行流程,這一節,我們來學習 etcd 寫請求執行流程。

1、etcd寫請求概覽

etcd 一個寫請求執行流程又是怎樣的呢?

etcdctl 	put	 hello	world	‐‐endpoints	192.168.65.210:2379

執行流程:

  • 1、首先 client 端通過負載均衡演算法選擇一個 etcd 節點,發起 gRPC 調用;

  • 2、然後 etcd 節點收到請求後經過 gRPC 攔截器、Quota 模塊後,進入 KVServer 模塊;

  • 3、KVServer 模塊向 Raft 模塊提交一個提案,提案內容為“大家好,請使用 put 方法執行一個 key 為 hello,value 為 world 的命令”。

  • 4、隨後此提案通過 Raft HTTP 網路模塊轉發、經過集群多數節點持久化後,狀 態會變成已提交;

  • 5、etcdserver 從 Raft 模塊獲取已提交的日誌條目,傳遞給 Apply 模塊

  • 6、Apply 模塊通過 MVCC 模塊執行提案內容,更新狀態機。

與讀流程不一樣的是,寫流程還涉及 Quota、WAL、Apply 三個模塊。etcd 的 crash-safe 及冪等性也正是基於 WAL 和 Apply 流程的 consistent index 等實現的。

2、詳細步驟解讀

2.1、Quota 模塊

流程二

Quota 模塊主要用於檢查下當前 etcd db 大小加上你請求的 key-value 大小之和是否超過 了配額(quota-backend-bytes)。 如果超過了配額,它會產生一個告警(Alarm)請求,告警類型是 NO SPACE,並通過 Raft 日誌同步給其它節點,告知 db 無空間了,並將告警持久化存儲到 db 中。最終,無論 是 API 層 gRPC 模塊還是負責將 Raft 側已提交的日誌條目應用到狀態機的 Apply 模塊, 都拒絕寫入,集群只讀

常見的 etcdserver: mvcc: database space exceeded 錯誤就是因為Quota 模塊檢測到 db 大小超限導致的。哪些情況會觸發這個錯誤:

  • 一方面預設 db 配額僅為 2G,當你的業務數據、寫入 QPS、Kubernetes 集群規模增大後,你的 etcd db 大小就可能會超過 2G。

  • 另一方面 etcd 是個 MVCC 資料庫,保存了 key 的歷史版本,當你未配置壓縮策略的時候,隨著數據不斷寫入,db 大小會不斷增大,導致超限。

解決辦法:

1、首先當然是調大配額,etcd 社區建議不超過 8G。

如果填的是個小於 0 的數,就會禁用配額功能,這可能會讓db 大小處於失控,導致性能下降,不建議你禁用配額。

2、檢查 etcd 的壓縮(compact)配置是否開啟、配置是否合理。

壓縮時只會給舊版本Key打上空閑(Free)標記,後續新的數據寫入的時候可復用這塊空間,db大小並不會減小。

如果需要回收空間,減少 db 大小,得使用碎片整理 (defrag), 它會遍歷舊的 db 文件數據,寫入到一個新的 db 文 件。但是它對服務性能有較大影響,不建議你在生產集群頻繁使 用。

調整後還需要手動發送一個取消告警(etcdctl alarm disarm)的命令,以消除所有告警, 否則因為告警的存在,集群還是無法寫入。

這個註意別忘記了!!!

查看狀態
etcdctl --endpoints=192.168.91.68:2379,192.168.91.68:12379,192.168.91.68:22379 endpoint status -w table

壓縮
etcdctl --endpoints=192.168.91.68:2379,192.168.91.68:12379,192.168.91.68:22379 compact 1

碎片整理
etcdctl --endpoints=192.168.91.68:2379,192.168.91.68:12379,192.168.91.68:22379 defrag


etcdctl alarm disarm

2.2、KVServer 模塊

流程3

通過流程二的配額檢查後,請求就從 API 層轉發到了流程三的 KVServer 模塊的 put 方 法

KVServer 模塊主要功能為:
1、打包提案: 將 put 寫請求內容打包成一個提案消息,提交給 Raft 模塊

2、請求限速、檢查: 不過在提交提案前,還有限速、鑒權和大包檢查

2.2.1、Preflight Check

為了保證集群穩定性,避免雪崩,任何提交到 Raft 模塊的請求,都會做一些簡單的限速判斷。

限速

  • 如果 Raft 模塊已提交的日誌索引(committed index)已應用到狀態機的日誌索引 (applied index)超過了 5000,那麼它就返回一個etcdserver: too many requests錯誤給 client。

鑒權

  • 然後它會嘗試去獲取請求中的鑒權信息,若使用了密碼鑒權、請求中攜帶了 token,如果 token 無效,則返回auth: invalid auth token 錯誤給 client。

大包檢查

  • 其次它會檢查你寫入的包大小是否超過預設的 1.5MB, 如果超過了會返回etcdserver: request is too large錯誤給 client。

2.3、Propose

流程4

通過檢查後會生成一個唯一的 ID,將此請求關聯到一個對應的消息通知 channel(用於接收結果),然後向 Raft 模塊發起(Propose)一個提案(Proposal)。 Raft 模塊發起提案後,KVServer 模塊會等待此 put 請求,等待寫入結果通過消息通知 channel 返回或者超時。

etcd 預設超時時間是 7 秒(5 秒磁碟 IO 延時 +2*1 秒競選超時 時間),如果一個請求超時未返回結果,則可能會出現你熟悉的etcdserver: request timed out 錯誤。

2.4 WAL 模塊

流程5

Raft 模塊收到提案後,如果當前節點是 Follower,它會轉發給 Leader,只有 Leader 才能 處理寫請求

Leader 收到提案後,通過 Raft 模塊輸出待轉發給 Follower 節點的消息和待持久化的日誌 條目,日誌條目則封裝了我們上面所說的 put hello 提案內容。

etcdserver 從 Raft 模塊獲取到以上消息和日誌條目後,作為 Leader,它會將 put 提案消 息廣播給集群各個節點,同時需要把集群 Leader 任期號、投票信息、已提交索引、提案內 容持久化到一個 WAL(Write Ahead Log)日誌文件中,用於保證集群的一致性、可恢復 性,也就是我們圖中的流程五模塊。

2.4.1、WAL 日誌結構

WAL 日誌結構如下:

WAL 文件它由多種類型的 WAL 記錄順序追加寫入組成,每個記錄由類型、數據、迴圈冗 餘校驗碼組成不同類型的記錄通過 Type 欄位區分,Data 為對應記錄內容,CRC 為迴圈 校驗碼信息

WAL 記錄類型目前支持 5 種,分別是文件元數據記錄、日誌條目記錄、狀態信息記錄、 CRC 記錄、快照記錄:

  • 文件元數據記錄:包含節點 ID、集群 ID 信息,它在 WAL 文件創建的時候 寫入;

  • 日誌條目記錄:包含 Raft 日誌信息,如 put 提案內容;

  • 狀態信息記錄:包含集群的任期號、節點投票信息等,一個日誌文件中會有 多條,以最後的記錄為準;

  • CRC 記錄:包含上一個 WAL 文件的最後的 CRC(迴圈冗餘校驗碼)信息, 在創建、切割 WAL 文件時,作為第一條記錄寫入到新的 WAL 文件, 用於校驗數據 文件的完整性、準確性等;

  • 快照記錄:包含快照的任期號、日誌索引信息,用於檢查快照文件的準確性。

2.4.2、WAL 持久化

首先會將 put 請求封裝成一個 Raft 日誌條目,Raft 日誌條目的數據結構信息如下:

type Entry struct {
   Term             uint64    `protobuf:"varint,2,opt,name=Term" json:"Term"`
   Index            uint64    `protobuf:"varint,3,opt,name=Index" json:"Index"`
   Type             EntryType `protobuf:"varint,1,opt,name=Type,enum=Raftpb.EntryType" json:"Type"`
   Data             []byte    `protobuf:"bytes,4,opt,name=Data" json:"Data,omitempty"`
}

它由以下欄位組成:

  • Term 是 Leader 任期號,隨著 Leader 選舉增加;
  • Index 是日誌條目的索引,單調遞增增加;
  • Type 是日誌類型,比如是普通的命令日誌(EntryNormal),還是集群配置變更日誌(EntryConfChange);
  • Data 保存我們上面描述的 put 提案內容。

具體持久化過程如下:
1、它首先先將 Raft 日誌條目內容(含任期號、索引、提案內容)序列化後保存到 WAL 記錄的 Data 欄位, 然後計算 Data 的 CRC 值,設置 Type 為 Entry Type, 以上信息就組成了一個完整的 WAL 記錄。

2、最後計算 WAL 記錄的長度,順序先寫入 WAL 長度(Len Field),然後寫 入記錄內容,調用 fsync 持久化到磁碟,完成將日誌條目保存到持久化存儲中。

3、當一半以上節點持久化此日誌條目後, Raft 模塊就會通過 channel 告知 etcdserver 模塊,put 提案已經被集群多數節點確認,提案狀態為已提交,你可以執 行此提案內容了。

4、於是進入流程六,etcdserver 模塊從 channel 取出提案內容,添加到先進 先出(FIFO)調度隊列,隨後通過 Apply 模塊按入隊順序,非同步、依次執行提案內 容。

2.5、Apply 模塊

流程 7

Apply 模塊主要用於執行處於 已提交狀態的提案,將其更新到狀態機

Apply 模塊在執行提案內容前,首先會判斷當前提案是否已經執行過了,如果執行了則直 接返回,若未執行同時無 db 配額滿告警,則進入到 MVCC 模塊,開始與持久化存儲模塊 打交道。

如果執行過程中 crash,重啟後如何找回異常提案,再次執行的呢?

主要依賴 WAL 日誌,因為提交給 Apply 模塊執行的提案已獲得多數節點確認、持久化, etcd 重啟時,會從 WAL 中解析出 Raft 日誌條目內容,追加到 Raft 日誌的存儲中,並重 放已提交的日誌提案給 Apply 模塊執行。

重啟恢復時,如何確保冪等性,防止提案重覆執行導致數據混亂呢?

etcd 通過引入一個 consistent index 的欄位,來存儲系統當前已經執行過的日誌條目索 引,實現冪等性。

因為 Raft 日誌條目中的索引(index)欄位,而且是全局單調遞增的,每個日誌條目索引 對應一個提案。 如果一個命令執行後,我們在 db 裡面也記錄下當前已經執行過的日誌條 目索引,就可以解決冪等性問題了。當然還需要將執行命令和記錄index這兩個操作作為原子性事務提交,才能實現冪等

2.6、MVCC 模塊

流程 8 和 9

MVCC 主要由兩部分組成,一個是記憶體索引模塊 treeIndex,保存 key 的歷史版本號信 息,另一個是 boltdb 模塊,用來持久化存儲 key-value 數據

MVCC 模塊執行 put hello 為 world 命令時,它是如何構建記憶體索引和保存哪些數據到 db呢?

2.6.1、treeIndex

MVCC 寫事務在執行 put hello 為 world 的請求時,會基於 currentRevision 自增生成新 的 revision, 如{2,0},然後從 treeIndex 模塊中查詢 key 的創建版本號、修改次數信息。這 些信息將填充到 boltdb 的 value 中,同時將用戶的 hello key 和 revision 等信息存儲到 B-tree,也就是下麵簡易寫事務圖的流程一,整體架構圖中的流程八。

hello: revision{2,0}

這裡的 2,0 具體指的是什麼呢?

這裡不太懂,有清楚的朋友,請不吝賜教。

2.6.2、boltdb

MVCC 寫事務自增全局版本號後生成的 revision{2,0},它就是 boltdb 的 key,通過它就 可以往 boltdb 寫數據了,進入了整體架構圖中的流程九。

那麼寫入 boltdb 的 value 含有哪些信息呢?

寫入 boltdb 的 value, 並不是簡單的"world",如果只存一個用戶 value,索引又是保存 在易失的記憶體上,那重啟 etcd 後,我們就丟失了用戶的 key 名,無法構建 treeIndex 模塊 了。

因此為了構建索引和支持 Lease(租約) 等特性,etcd 會持久化以下信息:

  • key 名稱;

  • key 創建時的版本號(create_revision)、最後一次修改時的版本號 (mod_revision)、key 自身修改的次數(version);

  • value 值;

  • 租約信息。

boltdb value 的值就是將含以上信息的結構體序列化成的二進位數據,然後通過 boltdb 提供的 put 介面,etcd 就快速完成了將你的數據寫入 boltdb。

註意: 在以上流程中,etcd 並未提交事務(commit),因此數據只更新在 boltdb 所管理 的記憶體數據結構中。

事務提交的過程,包含 B+tree 的平衡、分裂,將 boltdb 的臟數據(dirty page)、元數 據信息刷新到磁碟,因此事務提交的開銷是昂貴的。

如果我們每次更新都提交事務,etcd寫性能就會較差。

etcd 的解決方案是合併再合併:

  • 首先 boltdb key 是版本號,put/delete 操作時,都會基於當前版本號遞增生成新的版本 號,因此屬於順序寫入,可以調整 boltdb 的 bucket.FillPercent 參數,使每個 page 填充 更多數據,減少 page 的分裂次數並降低 db 空間。
  • 其次 etcd 通過合併多個寫事務請求,通常情況下,是非同步機制定時(預設每隔 100ms) 將批量事務一次性提交(pending 事務過多才會觸發同步提交), 從而大大提高吞吐量。但是這優化又引發了另外的一個問題, 因為事務未提交,讀請求可能無法從 boltdb 獲取 到最新數據
  • 為瞭解決這個問題,etcd 引入了一個 bucket buffer 來保存暫未提交的事務數據。在更新 boltdb 的時候,etcd 也會同步數據到 bucket buffer。因此 etcd 處理讀請求的時候會優 先從 bucket buffer 裡面讀取,其次再從 boltdb 讀,通過 bucket buffer 實現讀寫性能提 升,同時保證數據一致性。

以上就是 etcd 寫請求執行的流程,自己還是有蠻多地方不太懂,常看常新吧。

學習鏈接

b站-etcd寫請求執行流程-圖靈教程


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • [TOC] ___ #作用 - 在請求AuthorizeFilter -> ResourceFilter -> ActionFilter, 可以記錄Action的日誌 - ActionFilter 在控制器實例化之後執行 - ResourceFilter 可以在全局, Controller, Act ...
  • # 引言 上一篇中[.Net 編譯器平臺 Roslyn](https://niuery.com/post/67),介紹了Roslyn的各項功能,包括公開API,使用語法,使用語義,使用工作區等功能。 那麼回到上一篇中提到的問題,實現類似這樣的功能(以下代碼為偽代碼): ```csharp strin ...
  • ## - 結論 先上結論,答案是yes,C#中數組確實具有out參數的特性。 ## - 疑問 最近開發一個上位機的功能,有段代碼看得我一直很迷糊,我的認識,函數的執行結果,要麼在函數中通過return返回,要麼通過out或ref參數返回。這段代碼中明顯沒有通過return獲取返回值,輸入參數倒是看起 ...
  • # Unity中的RegisterPlugins:深入解析與實用案例 在Unity游戲開發中,我們經常需要使用第三方插件來實現一些特定的功能。為了讓這些插件能夠在Unity中正常工作,我們需要對它們進行註冊。本文將詳細介紹Unity中的`RegisterPlugins`方法,並通過三個實用案例來展示 ...
  • 本章將和大家分享 ASP.NET Core SignalR 中的中心(服務端)。 本文大部分內容摘自微軟官網:https://learn.microsoft.com/zh-cn/aspnet/core/signalr/hubs?view=aspnetcore-7.0 廢話不多說,我們直接來看一個De ...
  • # UGUI的Text(文本)組件的介紹及使用 ## 什麼是UGUI的Text(文本)組件? UGUI(Unity Graphic User Interface)是Unity引擎的一套用戶界面系統,而Text(文本)組件是UGUI中用於在游戲界面中顯示文本的組件。該組件可以用於顯示游戲中的文字、數字 ...
  • # 使用Mailx發送郵件 > 環境:CentOS 7 > > 1.安裝mailx > > ``` > yum install mailx -y > ``` > > 2.修改/etc/mail.rc文件 > > ``` > vim /etc/mail.rc > `在最末尾寫入如下三行 (xxx為你的 ...
  • # Centos7中禁止root用戶遠程登錄和修改登錄埠 ## 介紹 > Linux中root用戶許可權比較大,被不法人員獲知賬戶和密碼後,用root登錄後,可以對伺服器做任何操作,對伺服器危害較大,故需要禁止root用戶登錄,並且修改登錄埠,這樣就算root密碼泄露,埠不是預設,也無法登錄服務 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...