Orleans 中的叢集管理

Orleans 透過內建成員資格通訊協定提供叢集管理,我們有時會將其稱為「定址接收器成員資格」。 此通訊協定的目標是讓所有定址接收器 (Orleans 伺服器) 同意目前運作的定址接收器集、偵測失敗的定址接收器,並允許新的定址接收器加入叢集。

通訊協定仰賴外部服務來提供 IMembershipTable 的抽象概念。 IMembershipTable 是一般 No-SQL-like 持久性資料表,我們用於兩個用途。 首先,其會做為會合點,讓定址接收器彼此尋找,而 Orleans 用戶端則尋找定址接收器。 其次,其會用來儲存目前的成員資格檢視 (現存定址接收器的清單),並協助協調成員資格檢視的合約。 我們目前有 6 個 IMembershipTable 的實作:以 Azure Table Storage、SQL Server、Apache ZooKeeperConsul IOAWS DynamoDB,以及記憶體內部模擬為基礎進行開發。

除了 IMembershipTable 之外,每個定址接收器還參與一個完全分散式的對等成員資格通訊協定,以偵測失敗的定址接收器,並達到一組現存定址接收器的協定。 我們一開始會先描述以下 Orleans 成員資格通訊協定的內部實作,稍後再描述 IMembershipTable 的實作。

基本成員資格通訊協定

  1. 啟動時,每個定址接收器都會使用的 IMembershipTable 實作,將本身的輸入新增至已知的共用資料表中。 定址接收器身分識別 (ip:port:epoch) 與服務部署識別碼的組合,會用來做為資料表中的唯一索引鍵。 Epoch 只是這個定址接收器啟動時的時間刻度,因此 ip:port:epoch 在給定 Orleans 部署中保證是唯一的。

  2. 定址接收器會透過應用程式 Ping 直接互相監視 (「您正在運作」heartbeats)。 Ping 會透過定址接收器通訊的相同 TCP 通訊端,以做為從定址接收器傳送到定址接收器的直接訊息。 如此一來,Ping 會與實際的網路問題和伺服器健康情況完全相互關聯。 每個定址接收器都會 Ping 一組可設定的其他定址接收器。 定址接收器會藉由計算其他定址接收器身分識別上的一致雜湊、形成所有身分識別的虛擬通道,並在通道上挑選 X 後續定址接收器 (這是稱為一致雜湊的已知分散式技術,並廣泛使用於許多分散式雜湊表中,例如 Chord DHT)。

  3. 如果接收器 S 未從受監視的伺服器 P 取得 Y Ping 回覆,其就會懷疑並將時間戳記的懷疑寫入 IMembershipTable 中 P 的資料列。

  4. 如果 P 在 K 秒內有超過 Z 個懷疑,則 S 會將該 P 寫入 P 資料列,並廣播所有定址接收器的要求,以重新讀取成員資格資料表 (無論如何其仍會定期進行)。

  5. 如需詳細資料:

    1. 懷疑會寫入至 IMembershipTable (在對應至 P 的資料列特殊資料行中)。當 S 懷疑 P 時,其會寫入:「在 TTT S 懷疑 P 時」。

    2. 其中一個懷疑不足以將 P 宣告為無效。 您在可設定的時間範圍 T 中,需要不同定址接收器中的 Z 懷疑,通常是 3 分鐘,才能將 P 宣告為無效。 此懷疑是使用 IMembershipTable 所提供的開放式並行存取控制來撰寫。

    3. 懷疑定址接收器 S 會讀取 P 的資料列。

    4. 如果 S 是最後一個懷疑者 (在 T 期間內已有 Z-1 個懷疑者,如懷疑資料行中所撰寫),則 S 決定將 P 宣告為無效。 在此情況下,S 會將本身新增至懷疑者清單,並在 P 的 [狀態] 資料行中寫入 P 為 [無效]。

    5. 否則,如果 S 不是最後一個懷疑者,則 S 只會將本身新增至懷疑者的資料行。

    6. 在任一情況下,回寫都會使用已讀取的版本號碼或 ETag,因此會將此資料列的更新序列化。 如果因版本/ETag 不符而寫入失敗,則 S 會重試 (再次讀取,並嘗試寫入,除非 P 已標示為無效)。

    7. 在高階中,「讀取、本機修改、回寫」這個序列為交易。 不過,我們不會使用儲存體交易來執行此動作。 「交易」程式碼會在伺服器上本機執行,且我們會使用 IMembershipTable 所提供的開放式同步存取來確保隔離和不可部分完成性。

  6. 每個定址接收器都會定期讀取其部署的整個成員資格資料表。 如此一來,定址接收器會了解加入的新定址接收器,以及宣告為無效的定址接收器。

  7. 組態:我們提供預設組態,這是在 Azure 中生產環境使用期間進行手動調整。 目前,預設值為:每個定址接收器都會受到其他三個定址接收器的監視,兩個懷疑就足以宣告定址接收器無效,懷疑僅來自過去三分鐘 (否則懷疑會過期)。 Ping 每 10 秒會傳送一次,而您必須錯過三個 Ping 以懷疑定址接收器。

  8. 強制執行完美失敗偵測 – 如果定址接收器遺失與其他定址接收器的通訊,而定址接收器程序本身仍在執行中,則理論上可能宣告定址接收器為無效的。 若要解決此問題,一旦定址接收器在資料表中宣告為無效後,所有人員隨即會將其視為無效,即使其並非無效 (只是暫時分割或活動訊號訊息遺失) 也一樣。 所有人員都會停止與其通訊,且一旦得知其無效後 (方法是從資料表讀取其新狀態),隨即會認定無效並關閉其程序。 因此,基礎結構必須就位,才能重新啟動定址接收器做為新的程序 (在啟動時會產生新的 Epoch 編號)。 當其裝載於 Azure 時,就會自動發生。 若非如此,則需要另一個基礎結構。 例如,Windows 服務設定為在失敗或 Kubernetes 部署時自動重新啟動。

  9. 最佳化以減少定期讀取資料表的頻率,並加速所有定址接收器了解新的聯結定址接收器和無效定址接收器。 每當任何定址接收器將任何項目成功寫入資料表 (懷疑、新的聯結等等) 時,也會廣播至所有其他定址接收器 –「立即繼續重新讀取資料表」。 定址接收器「不會」向其他人員告知其在資料表中撰寫的內容 (因為這項資訊可能已經過期/錯誤),其只會告知人員重新讀取資料表。 如此一來,我們即可非常快速地了解成員資格變更,而不必等候完整的定期閱讀週期。 我們仍需要定期讀取,以防「重新讀取資料表」訊息遺失。

基本成員資格通訊協定的屬性

  1. 可以處理任何失敗次數

    我們的演算法可以處理任何失敗次數 (也就是 f<=n),包括完整叢集重新啟動。 這與「傳統」Paxos 型解決方案相反,這種解決方案需要仲裁,通常是多數。 在生產環境中,當超過一半的定址接收器關閉時,我們就會看到這個情況。 我們的系統保持運作,而 Paxos 型成員資格無法有所進度。

  2. 資料表的流量非常少

    實際的 Ping 會直接在伺服器之間移動,而不是移至資料表。 這會產生許多流量,且從失敗偵測的觀點來看,會較為不精確 - 如果定址接收器無法觸達資料表,則會遺漏寫入其「我正在運作」的活動訊號,而其他人會加以終止。

  3. 可調整性與完整性

    雖然您無法同時達到完美且精確的失敗偵測,但通常希望能夠以精確性 (不希望將處於運作中的定址接收器宣告為無效) 來換取完整性 (希望盡速將確實無效的定址接收器宣告無效)。 可設定的投票會宣告無效和遺漏的 Ping,以允許交易這兩者。 如需詳細資訊,請參閱耶魯大學:電腦科學失敗偵測器

  4. Scale \(規模\):

    基本通訊協定可以處理數千部,甚至可能處理數十萬部伺服器。 這與傳統的 Paxos 型解決方案相反,例如群組通訊協定,這些通訊協定已知不會調整超過數十個。

  5. 診斷:

    資料表也非常便於診斷和疑難排解。 系統管理員可以立即在資料表中找到目前運作的定址接收器清單,以及查看所有已終止的定址接收器和懷疑的歷程記錄。 這在診斷問題時特別有用。

  6. 為什麼我們需要可靠的持續性儲存體,才能實作 IMembershipTable

    我們會就兩個用途,針對 IMembershipTable 使用永續性儲存體 (Azure 資料表、SQL Server、AWS DynamoDB、Apache ZooKeeper 或 Consul IO KV)。 首先,其會做為會合點,讓定址接收器彼此尋找,而 Orleans 用戶端則尋找定址接收器。 其次,我們會使用可靠的儲存體來協助我們協調成員資格檢視的合約。 雖然我們會在定址接收器之間直接以點對點方式執行失敗偵測,但我們會將成員資格檢視儲存在可靠的儲存體中,並使用此儲存體所提供的並行控制機制,來達到作用中和無效的合約。 如此一來,我們的通訊協定就會將分散式共識的難題委外給雲端。 因為我們會完全利用基礎雲端平台的強大功能,以真正做為平台即服務 (PaaS)。

  7. 如果資料表有一段時間無法存取,會發生什麼事

    當儲存體服務關閉、無法使用或發生通訊問題時,Orleans 通訊協定「不會」將定址接收器宣告為無效。 操作定址接收器會持續運作,而不會發生任何問題。 不過,Orleans 無法宣告定址接收器無效 (如果是透過遺漏的 Ping 偵測到某些定址接收器無效,就無法將這個事實寫入資料表),也無法允許新的定址接收器加入。 因此,完整性會受到影響,但精確度則不會 — 從資料表分割一律不會造成 Orleans 誤將定址接收器宣告為無效。 此外,如果為部分網路分割區 (如果某些定址接收器可以存取資料表,但有些無法),Orleans 就會將無效的定址接收器宣告為無效,但所有其他定址接收器可能需要一些時間才能知情。 偵測可能會延遲,但 Orleans 絕不會因為資料表無法使用而錯誤地終止定址接收器。

  8. 僅限診斷的直接 IAmAlive 寫入資料表

    除了在定址接收器之間傳送的活動訊號之外,每個定址接收器也會在資料表中定期更新其資料列中的「我正在運作中」資料行。 這個「我正在運作中」資料行僅用於「手動疑難排解和診斷」,且不會由成員資格通訊協定本身使用。 通常會以較低的頻率 (每 5 分鐘一次) 撰寫,並做為系統管理員檢查叢集的即時性,或輕鬆地找出定址接收器上次運作的時間。

排序成員資格檢視的延伸模組

上述基本成員資格通訊協定稍後已擴充,以支援已排序的成員資格檢視。 我們將簡短描述此延伸模組的原因及其實作方式。 延伸模組不會變更上述設計中的任何項目,只要新增所有成員資格設定全域總體排序的屬性即可。

為什麼排序成員資格檢視很有用?

  • 這可將新定址接收器與叢集的聯結進行序列化。 如此一來,當新的定址接收器加入叢集時,其可以驗證已啟動的所有其他定址接收器的雙向連線能力。 如果其中某些已聯結的定址接收器未回應 (可能表示新定址接收器的網路連線問題),則不允許加入新的定址接收器。 這可確保在定址接收器啟動時,叢集中的所有定址接收器之間至少有完整的連線能力 (這已經過實作)。

  • 定址接收器中的較高層級通訊協定 (例如分散式粒紋目錄) 可以利用成員資格檢視已排序的事實,並使用這項資訊來執行更聰明的重複啟用解析。 特別是,當目錄發現 2 個啟用是在成員資格處於 Flux 狀態時建立,其可能會決定停用根據現在過期的成員資格資訊所建立的較舊啟用。

擴充成員資格通訊協定:

  1. 針對此功能的實作,我們會利用對 MembershipTable 所提供多個資料列的交易支援。

  2. 我們會將成員資格版本資料列新增至追蹤資料表變更的資料表。

  3. 當定址接收器 S 需要撰寫定址接收器 P 的懷疑或無效宣告時:

    1. S 會讀取最新的資料表內容。 如果 P 已經無效,則不執行任何動作。 否則,
    2. 在相同的交易中,將變更寫入 P 的資料列,以及遞增版本號碼,然後將其寫回資料表。
    3. 這兩個寫入都需受 ETag 制約。
    4. 如果交易因 P 資料列或版本資料列中的 ETag 不符而中止,請再次嘗試。
  4. 所有寫入資料表都會修改及遞增版本資料列。 如此一來,所有對資料表的寫入都會加以序列化 (透過序列化版本資料列的更新),且因為定址接收器只會遞增版本號碼,所以寫入也會依遞增順序進行總體排序。

擴充成員資格通訊協定的延展性:

在通訊協定的擴充版本中,所有寫入都會透過一個資料列進行序列化。 這可能會損害叢集管理通訊協定的延展性,因為其會增加並行資料表寫入之間的衝突風險。 若要部分減輕此問題,定址接收器會使用指數輪詢,重試其所有寫入資料表。 我們已觀察到擴充的通訊協定可在 Azure 的生產環境中順暢地運作,最多可有 200 個定址接收器。 不過,我們認為通訊協定在擴充超過千個定址接收器時可能會發生問題。 在這類大型設定中,可輕鬆地停用版本資料列的更新,基本上會維護叢集管理通訊協定的其餘部分,並放棄總體排序屬性。 另請注意,我們會在這裡參考叢集管理通訊協定的延展性,而不是 Orleans 的其餘部分。 我們相信 Orleans 執行階段的其他部分 (傳訊、分散式目錄、粒紋裝載、用戶端對閘道連線能力) 可擴充遠超過數百個定址接收器。

成員資格 [資料表]

如前所述,IMembershipTable 可用來做為會合點,以供定址接收器尋找彼此,以及供 Orleans 用戶端尋找定址接收器,並協助協調成員資格檢視的合約。 我們目前有六個 IMembershipTable 的實作:以 Azure Table、SQL Server、Apache ZooKeeper、Consul IO、AWS DynamoDB,以及記憶體內部模擬為基礎進行開發。

  1. Azure 資料表儲存體 - 在此實作中,我們會使用 Azure 部署識別碼做為分割區索引鍵,以及定址接收器身分識別 (ip:port:epoch) 做為資料列索引鍵。 這些可共同保證每個定址接收器的唯一索引鍵。 針對並行控制,我們會使用以 Azure 資料表 ETag 為基礎的開放式並行存取控制。 每次從資料表讀取時,我們都會儲存每個讀取資料列的 ETag,並在嘗試回寫時使用該 ETag。 每次寫入時,Azure 資料表服務會自動指派及檢查 ETag。 針對多資料列交易,我們會利用 Azure 資料表所提供的批次交易支援,其會保證透過具有相同分割區索引鍵的資料列進行可序列化交易。

  2. 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),其目前用來插入統計資料。 您可以在 CreateOrleansTables_SqlServer.sql 中看到 SQL Server 的確切實作和原理。

  3. Apache ZooKeeper - 在此實作中,我們會使用設定的部署識別碼做為根節點,而定址接收器身分識別 (ip:port@epoch) 做為其子節點。 這些可共同保證每個定址接收器的唯一路徑。 針對並行控制,我們會使用以節點版本為基礎的開放式並行存取控制。 每次從部署根節點讀取時,我們都會儲存每個讀取子定址接收器節點的版本,並在嘗試回寫時使用該版本。 每次節點的資料變更時,ZooKeeper 服務都會以不可部分完成的方式增加版本號碼。 針對多資料列交易,我們會使用多重方法,其可保證透過具有相同父部署識別碼節點的定址接收器節點進行可序列化交易。

  4. Consul IO - 我們使用 Consul 的索引鍵/值存放區來實作成員資格資料表。 如需詳細資料,請參閱 Consul-Deployment

  5. AWS DynamoDB - 在此實作中,我們會使用叢集部署識別碼做為分割區索引鍵,而定址接收器身分識別 (ip-port-generation) 做為 RangeKey,讓記錄統一。 開放式同步存取是由 ETag 屬性所建立,方法是在 DynamoDB 上進行條件式寫入。 實作邏輯與 Azure 資料表儲存體相當類似。

  6. 用於開發設定的記憶體內部模擬。 我們會針對該實作使用稱為 MembershipTableGrain 的特殊系統粒紋。 此粒紋位於指定的主要定址接收器上,這只適用於開發設定。 在任何實際生產環境中,都不需要使用量主要定址接收器。

組態

成員資格通訊協定是透過 OrleansConfiguration.xml 檔案 Globals 區段中的 Liveness 元素來設定。 預設值是以 Azure 數年的生產使用量調整,我們相信其代表良好的預設設定。 一般而言,不需要變更這些預設值。

範例組態元素:

<Liveness ProbeTimeout="5s"
    TableRefreshTimeout="10s"
    DeathVoteExpirationTimeout="80s"
    NumMissedProbesLimit="3"
    NumProbedSilos="3"
    NumVotesForDeathDeclaration="2" />

會實作 4 種活躍度。 活躍度通訊協定的類型是透過 OrleansConfiguration.xml 檔案中 Globals 區段 SystemStore 元素的 SystemStoreType 屬性來進行設定。

  1. MembershipTableGrain:成員資格資料表會儲存在主要定址接收器的粒紋中。 這僅限開發設定
  2. AzureTable:成員資格資料表會儲存在 Azure 資料表中。
  3. SqlServer:成員資格資料表會儲存在關聯式資料庫中。
  4. ZooKeeper:成員資格資料表會儲存在 ZooKeeper ensemble 中。
  5. Consul:使用 MembershipTableAssembly = "OrleansConsulUtils" 設定為自訂系統存放區。 如需詳細資料,請參閱 Consul-Deployment
  6. DynamoDB:使用 MembershipTableAssembly = "OrleansAWSUtils" 設定為自訂系統存放區。

針對所有活躍度類型,會在 Globals.Liveness 元素中定義通用組態變數:

  1. ProbeTimeout:探查其他定址接收器活躍度時的秒數,或探查定址接收器傳送有關本身的「我正在運作中」活動訊號訊息的秒數。 預設值為 10 秒。
  2. TableRefreshTimeout:從成員資格資料表擷取更新的秒數。 預設值為 60 秒。
  3. DeathVoteExpirationTimeout:成員資格資料表中無效投票的到期時間 (以秒為單位)。 預設值為 120 秒
  4. NumMissedProbesLimit:來自定址接收器的遺漏「我正在運作中」活動訊號訊息數目,或導致懷疑此定址接收器為無效的未回復探查數目。 預設為 3。
  5. NumProbedSilos:每個定址接收器活躍度探查的定址接收器數目。 預設為 3。
  6. NumVotesForDeathDeclaration:將某些定址接收器宣告為無效所需的非過期投票數目 (最多應該是 NumMissedProbesLimit)。 預設為 2。
  7. UseLivenessGossip:是否要使用 Gossip 最佳化來加速散佈活躍度資訊。 預設為 true。
  8. IAmAliveTablePublishTimeout:此定址接收器正在運作中成員資格資料表定期寫入的秒數。 僅用於診斷。 預設值為 5 分鐘。
  9. NumMissedTableIAmAliveLimit:資料表中遺漏的「我正在運作中」更新數目,來自導致所要記錄警告的定址接收器。 不會影響活躍度通訊協定。 預設為 2。
  10. MaxJoinAttemptTime:放棄之前嘗試加入定址接收器叢集的秒數。 預設值為 5 分鐘。
  11. ExpectedClusterSize:叢集的預期大小。 不需要非常精確,可能是高估的。 用來微調重試的指數輪詢演算法,以寫入 Azure 資料表。 預設為 20。

設計原理

可能會詢問的自然問題是為何不完全仰賴 Apache ZooKeeper 來進行叢集成員資格實作,可能是使用其現成支援搭配暫時節點的群組成員資格? 為什麼我們會同時實作成員資格通訊協定? 主要有三個原因:

  1. 雲端中的部署/裝載

    Zookeeper 並非裝載的服務 (至少在 2015 年 7 月撰寫時,且絕對當我們在 2011 年夏季第一次實作此通訊協定時,沒有任何 Zookeeper 版本以任何主要雲端服務提供者所託管服務身分執行)。 這表示在雲端環境 Orleans 中,客戶必須部署/執行/管理其 ZK 叢集的執行個體。 這只是另一個我們不想強制客戶執行的不必要負擔。 我們使用 Azure 資料表,仰賴託管的受控服務,讓客戶的生活更輕鬆。 基本上,在雲端中,使用雲端做為平台,而不是基礎結構。 另一方面,在執行內部部署和管理伺服器時,仰賴 ZK 做為實作 IMembershipTable 是可行的選項。

  2. 直接失敗偵測

    搭配暫時節點使用 ZK 的群組成員資格時,會在 Orleans 伺服器之間執行失敗偵測 (ZK 用戶端) 和 ZK 伺服器。 這不一定與 Orleans 伺服器之間的實際網路問題相互關聯。 我們希望失敗偵測能精確地反映通訊的叢集內部狀態。 具體來說,在我們的設計中,如果 Orleans 定址接收器無法與 IMembershipTable 通訊,則不會被視為無效,且可以繼續運作。 與其相反地,若我們使用 ZK 群組成員資格搭配暫時節點與 ZK 伺服器中斷連線,可能會導致 Orleans 定址接收器 (ZK 用戶端) 宣告為無效,但可能運作正常。

  3. 可攜性和彈性

    做為 Orleans 原理的一部分,我們不會強制對任何特定技術的強式相依性,而是具有彈性的設計,其中可使用不同的實作輕鬆地切換不同的元件。 這正是 IMembershipTable 抽象概念提供的目的。

致謝

我們會認可 Alex Kogan 對此通訊協定第一個版本的設計和實作貢獻。 此工作是在 2011 年夏季 Microsoft Research 的夏季實習中完成。 ZooKeeper 架構 IMembershipTable 實作是由 Shay Hazor 所完成,SQL IMembershipTable 的實作是由 Veikko Eeva 所完成,AWS DynamoDB IMembershipTable 的實作是由 Gutemberg Ribeiro 所完成,而 Consul 架構 IMembershipTable 的實作是由 Paul North 所完成。