Orleans 中的叢集管理
Orleans 透過內建的成員資格協議提供叢集管理,我們有時稱之為 叢集成員資格。 此通訊協定的目標是讓所有定址接收器 (Orleans 伺服器) 同意目前運作的定址接收器集、偵測失敗的定址接收器,並允許新的定址接收器加入叢集。
通訊協定仰賴外部服務來提供 IMembershipTable 的抽象概念。 IMembershipTable 是一張扁平且耐用的桌子,我們用於兩個用途。 首先,其會做為會合點,讓定址接收器彼此尋找,而 Orleans 用戶端則尋找定址接收器。 其次,其會用來儲存目前的成員資格檢視 (現存定址接收器的清單),並協助協調成員資格檢視的合約。
我們目前有多個 IMembershipTable實作:基於 Azure Table Storage、Azure Cosmos DB、ADO.NET (PostgreSQL、MySQL/MariaDB、SQL Server、Oracle)、Apache ZooKeeper、Consul IO、AWS DynamoDB、MongoDB、Redis、Apache Cassandra,以及用於開發的內存實作。
除了 IMembershipTable 之外,每個定址接收器還參與一個完全分散式的對等成員資格通訊協定,以偵測失敗的定址接收器,並達到一組現存定址接收器的協定。 我們在下文中描述 Orleans成員資格協議的內部實作。
成員協議規範
啟動時,每個定址接收器都會使用的 IMembershipTable 實作,將本身的輸入新增至已知的共用資料表中。 筒倉識別碼(
ip:port:epoch
)和服務部署識別碼(叢集識別碼)的結合用作數據表中的唯一鍵。 Epoch 只是這個定址接收器啟動時的時間刻度,因此ip:port:epoch
在給定 Orleans 部署中保證是唯一的。Silo 直接透過應用程式探針監視彼此(「您還活著嗎」
heartbeats
)。 探針會作為直接訊息在資料庫之間傳送,透過資料庫之間通訊使用的相同 TCP 套接字進行。 如此一來,探查就會與實際的網路問題和伺服器健康情況完全相互關聯。 每個儲存艙都會檢測一組可設定的其他儲存艙。 在一個資料儲存區中,透過計算其他資料儲存區識別碼的一致哈希來決定探測對象,形成一個包含所有識別碼的虛擬環狀結構,並在環狀結構上選擇 X 個後繼資料儲存區(這是一種被廣泛使用於許多分散式哈希表中的已知分散式技術,稱為 一致哈希,例如 Chord DHT)。如果資料筒倉 S 未從受監視的伺服器 P 收到 Y 次探查回應,則會將其懷疑的時間戳記寫入 IMembershipTable中 P 的資料列中。
如果 P 在 K 秒內有超過 Z 次懷疑,則 S 會將「P 已死」寫入 P 的行,並將當前成員表的快照傳送至所有其他資料庫。 資料筒倉會定期重新整理資料表,因此快照集是一種優化方式,可減少所有資料筒倉瞭解新成員檢視所需的時間。
如需詳細資料:
懷疑會寫入至 IMembershipTable (在對應至 P 的資料列特殊資料行中)。當 S 懷疑 P 時,其會寫入:「在 TTT S 懷疑 P 時」。
其中一個懷疑不足以將 P 宣告為無效。 您在可設定的時間範圍 T 中,需要不同定址接收器中的 Z 懷疑,通常是 3 分鐘,才能將 P 宣告為無效。 此懷疑是使用 IMembershipTable 所提供的開放式並行存取控制來撰寫。
懷疑定址接收器 S 會讀取 P 的資料列。
如果
S
是最後一個懷疑者 (在 T 期間內已有 Z-1 個懷疑者,如懷疑資料行中所撰寫),則 S 決定將 P 宣告為無效。 在此情況下,S 會將本身新增至懷疑者清單,並在 P 的 [狀態] 資料行中寫入 P 為 [無效]。否則,如果 S 不是最後一個懷疑者,則 S 只會將本身新增至懷疑者的資料行。
在任一情況下,回寫都會使用已讀取的版本號碼或 ETag,因此會將此資料列的更新序列化。 如果因版本/ETag 不符而寫入失敗,則 S 會重試 (再次讀取,並嘗試寫入,除非 P 已標示為無效)。
在高階中,「讀取、本機修改、回寫」這個序列為交易。 不過,我們不一定使用記憶體交易來執行這個動作。 「交易」程式碼會在伺服器上本機執行,且我們會使用 IMembershipTable 所提供的開放式同步存取來確保隔離和不可部分完成性。
每個定址接收器都會定期讀取其部署的整個成員資格資料表。 如此一來,定址接收器會了解加入的新定址接收器,以及宣告為無效的定址接收器。
快照廣播:為了降低定期資料表讀取的頻率,每次資料儲存區寫入資料表(例如懷疑、新加入等)時,都會將目前資料表狀態的快照傳送至所有其他資料儲存區。 由於成員資格數據表是一致且單調的版本設定,因此每個更新都會產生可安全地共用的唯一版本快照集。 這可讓您立即傳播成員資格變更,而不需要等待定期讀取週期。 當快照分發失敗時,定期讀取仍被保留作為後備機制。
已排序的成員檢視:成員協定可確保在全域範圍內完全排序所有成員配置。 此排序提供兩個主要優點:
保證連線:當新的數據孤島加入叢集時,它必須驗證與其他每個活躍中的數據孤島的雙向連線。 如果任何現有的 silo 沒有回應(可能表示網路連線問題),則不允許新的 silo 加入。 這可確保在啟動時叢集中所有尋址接收器之間的完整連線。 如需災害復原的例外狀況,請參閱下方關於IAmAlive的附註。
一致的目錄更新:較高層級的協定,例如分散式 grain 目錄,依賴於所有儲存區具有一致且單調的成員檢視。 這可讓您更有效地解決重複穀物的激活問題。 如需詳細資訊,請參閱 紋理目錄 文件。
實作詳細資料:
IMembershipTable 需要原子性更新,以確保變更的全域總順序:
- 實作必須以原子性方式更新表格項目(穀倉清單)和版本號碼。
- 這可以使用資料庫交易(如在 SQL Server 中)或使用 ETag 進行原子性比較和交換操作(如在 Azure 資料表儲存中)來實現。
- 特定機制取決於基礎記憶體系統的功能
資料表中的特殊成員資格版本資料列會追蹤變更:
- 每一個寫入數據表 (懷疑, 死亡宣告, 聯結) 會遞增此版本號碼
- 所有寫入都會使用原子性更新在這一行中串行化。
- 單調增加版本可確保所有成員資格變更的總順序
當筒倉 S 更新筒倉 P 的狀態時:
- S 會先讀取最新的數據表狀態
- 在單一原子操作中,它會同時更新 P 的資料列,並且遞增版本號碼。
- 如果原子更新失敗(例如,由於並發修改),則會使用指數退避重試作業。
延展性考慮:
透過版本列串行化所有寫入可能會影響可擴展性,因為爭用增加。 該通訊協定已在生產環境中驗證,能支持多達 200 個筒倉,但可能在超過一千個筒倉時面臨挑戰。 對於非常大的部署,即使成員資格更新成為瓶頸,Orleans 的其他部分(傳訊、粒紋目錄、主機托管)仍然保持可擴展性。
預設組態:在 Azure 的生產使用期間,已手動調整預設組態。 根據預設:每個儲存桶都會受到另外三個儲存桶的監視,兩個懷疑訊號足以宣告一個儲存桶為停止狀態,只考慮過去三分鐘內的懷疑,否則認為它們已過時。 探測信號每隔十秒傳送一次,而您需要錯過三個信號才會懷疑可能存在未探測到的區域。
自主監控:故障檢測器結合了 Hashicorp 的 Lifeguard 研究(論文、演講、部落格)的理念,以在發生大規模災變且大部分叢集經歷部分故障時改善叢集的穩定性。
LocalSiloHealthMonitor
元件會使用多個啟發式方法,為每個筒倉的健康狀況評分:- 成員資料表中的活躍狀態
- 沒有來自其他筒倉的懷疑
- 最近成功的探查回應
- 最近收到的探查要求
- 線程池的響應性(工作項目在 1 秒內執行)
- 定時器的準確性(在預定時間的 3 秒內觸發)
尋址接收器的健康分數會影響其探查逾時:與狀況良好的尋址接收器(分數 0)相比,狀況不良的尋址接收器(評分 1-8)增加了逾時。 這有兩個優點:
- 當網路或系統處於壓力時,讓探測有更長的時間來成功
- 讓不健康的孤島更有可能被投票淘汰,從而防止它們在錯誤地投票排除健康的孤島之前被淘汰。
這在發生如線程池耗盡的情況中特別有價值,因為當節點無法快速處理回應時,緩慢的節點可能會錯誤地懷疑那些正常運作的節點。
間接探查:另一個 Lifeguard啟發的功能,以減少不健康或分區的倉儲錯誤地將健康的倉儲宣告為故障的機會,來提高失敗偵測的準確性。 當監視筒倉在投票宣佈其目標筒倉已死亡之前,還剩兩次探測嘗試時,會採取間接偵測:
- 監視筒倉會隨機選取另一個筒倉作為中介,並要求其探查目標
- 中繼嘗試聯絡目標筒倉
- 如果目標在逾時期間內無法回應,則中介會傳送否定確認。
- 如果監視筒倉收到來自中介者的負面認可,而中介者宣佈自己狀況良好(如上所述通過自我監視),則監視筒倉會投票宣布目標死亡。
- 使用兩個必要投票的預設組態,間接探測的負面確認會計為兩票,從而在多個觀點確認故障時,更快速地宣告儲存單元故障。
強制執行完美的失敗偵測:一旦 silo 在數據表中宣告為死,即使它尚未死亡(只是暫時分割或心跳訊息丟失),它也會被視為死去。 所有人員都會停止與其通訊,且一旦得知其無效後 (方法是從資料表讀取其新狀態),隨即會認定無效並關閉其程序。 因此,基礎結構必須就位,才能重新啟動定址接收器做為新的程序 (在啟動時會產生新的 Epoch 編號)。 當其裝載於 Azure 時,就會自動發生。 若不是,則需要另一個基礎結構,例如設定為在失敗時自動重新啟動的 Windows 服務或 Kubernetes 部署。
如果資料表有一段時間無法存取,會發生什麼事:
當儲存體服務關閉、無法使用或發生通訊問題時,Orleans 通訊協定「不會」將定址接收器宣告為無效。 操作定址接收器會持續運作,而不會發生任何問題。 不過,Orleans 將無法宣告儲存槽死亡(如果它偵測到某些儲存槽透過遺漏的探查已經死亡,它將無法將這個事實寫入數據表),也無法允許新的儲存槽加入。 因此,完整性將受到影響,但精確度不會 — 從表格進行分割永遠不會造成 Orleans 錯誤地將孤立單位宣告為已死。 此外,如果為部分網路分割區 (如果某些定址接收器可以存取資料表,但有些無法),Orleans 就會將無效的定址接收器宣告為無效,但所有其他定址接收器可能需要一些時間才能知情。 偵測可能會延遲,但 Orleans 絕不會因為資料表無法使用而錯誤地終止定址接收器。
IAmAlive 用於進行診斷和災害復原的寫入:
除了在這些資源庫之間傳送的心跳信號之外,每個資源庫都會在資料表中自己的列裡定期更新「我還活著」的時間戳。 這有兩個用途:
- 針對診斷,它為系統管理員提供了一種簡單的方法來檢查叢集生存性,並確定節點上次活躍的時間。 時間戳通常會每隔 5 分鐘更新一次。
- 針對災害復原,如果某個穀倉在數個週期內(透過
NumMissedTableIAmAliveLimit
設定)未更新其時間戳,新穀倉在啟動連線檢查期間將忽略它,使叢集能夠從穀倉損毀而未適當清除的情況中復原。
成員資格 [資料表]
如前所述,IMembershipTable 可用來做為會合點,以供定址接收器尋找彼此,以及供 Orleans 用戶端尋找定址接收器,並協助協調成員資格檢視的合約。 主要 Orleans 存放庫包含許多系統的實作,例如 Azure 數據表記憶體、Azure Cosmos DB、PostgreSQL、MySQL/MariaDB、SQL Server、Apache ZooKeeper、Consul IO、Apache Cassandra、MongoDB、Redis、AWS DynamoDB,以及用於開發的記憶體內部實作。
Azure 資料表儲存體 - 在此實作中,我們會使用 Azure 部署識別碼做為分割區索引鍵,以及定址接收器身分識別 (
ip:port:epoch
) 做為資料列索引鍵。 這些可共同保證每個定址接收器的唯一索引鍵。 針對並行控制,我們會使用以 Azure 資料表 ETag 為基礎的開放式並行存取控制。 每次從資料表讀取時,我們都會儲存每個讀取資料列的 ETag,並在嘗試回寫時使用該 ETag。 每次寫入時,Azure 資料表服務會自動指派及檢查 ETag。 針對多資料列交易,我們會利用 Azure 資料表所提供的批次交易支援,其會保證透過具有相同分割區索引鍵的資料列進行可序列化交易。SQL Server - 在此實作中,設定的部署識別碼是用來區分部署,以及哪些定址接收器屬於哪些部署。 定址接收器身分識別會在適當的資料表和資料行中定義為
deploymentID, ip, port, epoch
的組合。 關聯式後端會使用開放式並行存取控制和交易,類似於在 Azure 資料表實作上使用 ETag 的程序。 關聯式實作預期資料庫引擎會產生所使用的 ETag。 在 SQL Server 的情況下,在 SQL Server 2000 上,所產生的 ETag 是從呼叫NEWID()
取得的 ETag。 在 SQL Server 2005 和更新版本,會使用 ROWVERSION。 Orleans 會讀取及寫入關聯式 ETag 做為不透明VARBINARY(16)
標記,並將其儲存在記憶體中做為 base64 編碼字串。 Orleans 支援使用UNION ALL
的多資料列插入 (適用於包括 DUAL 的 Oracle),其目前用來插入統計資料。 SQL Server 的確切實作和基本原理可以在 CreateOrleansTables_SqlServer.sql 上查看。Apache ZooKeeper - 在此實作中,我們會使用設定的部署識別碼做為根節點,而定址接收器身分識別 (
ip:port@epoch
) 做為其子節點。 這些可共同保證每個定址接收器的唯一路徑。 針對並行控制,我們會使用以節點版本為基礎的開放式並行存取控制。 每次從部署根節點讀取時,我們都會儲存每個讀取子定址接收器節點的版本,並在嘗試回寫時使用該版本。 每次節點的資料變更時,ZooKeeper 服務都會以不可部分完成的方式增加版本號碼。 針對多資料列交易,我們會使用多重方法,其可保證透過具有相同父部署識別碼節點的定址接收器節點進行可序列化交易。Consul IO - 我們使用 Consul 的索引鍵/值存放區來實作成員資格資料表。 如需詳細資料,請參閱 Consul-Deployment。
AWS DynamoDB - 在此實作中,我們會使用叢集部署識別碼做為分割區索引鍵,而定址接收器身分識別 (
ip-port-generation
) 做為 RangeKey,讓記錄統一。 開放式同步存取是由ETag
屬性所建立,方法是在 DynamoDB 上進行條件式寫入。 實作邏輯與 Azure 資料表儲存體相當類似。Apache Cassandra - 在此實作中,我們使用服務標識碼與叢集標識符的組合作為分區鍵,並將 Silo 身分識別(
ip:port:epoch
)作為列鍵。 它們一起保證每個儲存單元的唯一數據行。 針對並行控制,我們會使用基於靜態列版本的樂觀併發控制,配合輕量交易進行控制。 此版本數據行會針對分割區/叢集中的所有數據列共用,因此為每個叢集的成員資格數據表提供一致的遞增版本號碼。 此實作中沒有任何多行交易。用於開發設定的記憶體內部模擬。 我們會在該實作中使用特殊的系統顆粒。 此粒紋位於指定的主要定址接收器上,這只適用於開發設定。 在任何實際生產環境中,都不需要使用量主要定址接收器。
設計原理
可能會被問到的自然問題是,為什麼不完全依賴 Apache ZooKeeper 或 etcd 來實現叢集成員資格管理?這可能是因為使用 ZooKeeper 現成支援的暫時節點來進行群組成員資格管理。 為什麼我們會同時實作成員資格通訊協定? 主要有三個原因:
雲端中的部署/裝載:
Zookeeper 不是託管服務。 這表示在雲端環境 Orleans 中,客戶必須部署/執行/管理其 ZK 叢集的執行個體。 這只是另一個我們不想強制客戶執行的不必要負擔。 我們使用 Azure 資料表,仰賴託管的受控服務,讓客戶的生活更輕鬆。 基本上,在雲端中,使用雲端做為平台,而不是基礎結構。 另一方面,在執行內部部署和管理伺服器時,仰賴 ZK 做為實作 IMembershipTable 是可行的選項。
直接失敗偵測:
搭配暫時節點使用 ZK 的群組成員資格時,會在 Orleans 伺服器之間執行失敗偵測 (ZK 用戶端) 和 ZK 伺服器。 這不一定與 Orleans 伺服器之間的實際網路問題相互關聯。 我們希望失敗偵測能精確地反映通訊的叢集內部狀態。 具體來說,在我們的設計中,如果 Orleans 定址接收器無法與 IMembershipTable 通訊,則不會被視為無效,且可以繼續運作。 與其相反地,若我們使用 ZK 群組成員資格搭配暫時節點與 ZK 伺服器中斷連線,可能會導致 Orleans 定址接收器 (ZK 用戶端) 宣告為無效,但可能運作正常。
可攜性和彈性:
做為 Orleans 原理的一部分,我們不會強制對任何特定技術的強式相依性,而是具有彈性的設計,其中可使用不同的實作輕鬆地切換不同的元件。 這正是 IMembershipTable 抽象概念提供的目的。
成員資格通訊協議的屬性
可以處理任何失敗次數:
我們的演算法可以處理任何失敗次數 (也就是 f<=n),包括完整叢集重新啟動。 這與「傳統」Paxos 型解決方案相反,這種解決方案需要仲裁,通常是多數。 在生產環境中,當超過一半的定址接收器關閉時,我們就會看到這個情況。 我們的系統保持運作,而 Paxos 型成員資格無法有所進度。
資料表的流量非常少:
實際的探測直接在伺服器之間進行,而不涉及數據表。 這會產生許多流量,且從失敗偵測的觀點來看,會較為不精確 - 如果定址接收器無法觸達資料表,則會遺漏寫入其「我正在運作」的活動訊號,而其他人會加以終止。
可調整性與完整性:
雖然您無法同時達到完美且精確的失敗偵測,但通常希望能夠以精確性 (不希望將處於運作中的定址接收器宣告為無效) 來換取完整性 (希望盡速將確實無效的定址接收器宣告無效)。 可以設定的投票用於宣告探測項目失效或遺漏,允許作此兩者的處理。 如需詳細資訊,請參閱耶魯大學:電腦科學失敗偵測器。
Scale \(規模\):
通訊協定可以處理數千部甚至數萬部伺服器。 這與傳統的 Paxos 型解決方案相反,例如群組通訊協定,這些通訊協定已知不會調整超過數十個。
診斷:
資料表也非常便於診斷和疑難排解。 系統管理員可以立即在資料表中找到目前運作的定址接收器清單,以及查看所有已終止的定址接收器和懷疑的歷程記錄。 這在診斷問題時特別有用。
為什麼我們需要可靠的持續性儲存體,才能實作 IMembershipTable:
我們使用持久性儲存裝置來達成 IMembershipTable 的兩個目的。 首先,其會做為會合點,讓定址接收器彼此尋找,而 Orleans 用戶端則尋找定址接收器。 其次,我們會使用可靠的儲存體來協助我們協調成員資格檢視的合約。 雖然我們會在定址接收器之間直接以點對點方式執行失敗偵測,但我們會將成員資格檢視儲存在可靠的儲存體中,並使用此儲存體所提供的並行控制機制,來達到作用中和無效的合約。 如此一來,我們的通訊協定就會將分散式共識的難題委外給雲端。 因為我們會完全利用基礎雲端平台的強大功能,以真正做為平台即服務 (PaaS)。
僅限診斷的直接 IAmAlive 寫入資料表:
除了在定址接收器之間傳送的活動訊號之外,每個定址接收器也會在資料表中定期更新其資料列中的「我正在運作中」資料行。 這個「我正在運作中」資料行僅用於「手動疑難排解和診斷」,且不會由成員資格通訊協定本身使用。 通常會以較低的頻率 (每 5 分鐘一次) 撰寫,並做為系統管理員檢查叢集的即時性,或輕鬆地找出定址接收器上次運作的時間。
致謝
我們想要認可 Alex Kogan 對此通訊協定第一版的設計與實作的貢獻。 此工作是在 2011 年夏季 Microsoft Research 的夏季實習中完成。
基於 ZooKeeper 的 IMembershipTable 實作是由 Shay Hazor完成,SQL IMembershipTable 的實作是由 Veikko Eeva完成,AWS DynamoDB IMembershipTable 的實作是由 古滕貝格·里貝羅 完成,而基於 Consul 的 IMembershipTable 實作是由 Paul North完成的,最後,阿帕奇 Cassandra IMembershipTable 的實作是由 Arshia001改編自 OrleansCassandraUtils
。