共用方式為


使用異地備援設計高可用性應用程式

Azure 記憶體等雲端式基礎結構提供高可用性且持久的平台來裝載數據和應用程式。 雲端式應用程式的開發人員必須仔細考慮如何利用此平臺,為用戶發揮這些優勢。 Azure 記憶體提供異地備援選項,以確保即使在區域性中斷期間也能達到高可用性。 針對異地備援複寫設定的儲存體帳戶,會以同步方式複寫至主要區域,然後以非同步方式複寫至數百英里外的次要區域。

Azure 儲存體提供異地備援複寫的兩個選項:異地備援儲存體 (GRS)異地區域備援儲存體 (GZRS)。 若要使用 Azure 記憶體異地備援選項,請確定您的記憶體帳戶已針對讀取許可權異地備援記憶體 (RA-GRS) 或讀取許可權異地區域備援記憶體 (RA-GZRS) 進行設定。 如果不是,您可以深入瞭解如何 變更記憶體帳戶複寫類型

本文說明如何設計可繼續運作的應用程式,儘管容量有限,即使主要區域發生重大中斷也一樣。 如果主要區域變得無法使用,您的應用程式可以順暢地切換,以對次要區域執行讀取作業,直到主要區域再次響應為止。

應用程式設計考慮

當發生干擾主要區域讀取的問題時,您可以設計應用程式來處理暫時性錯誤或重大中斷。 當主要區域再次可用時,您的應用程式可以返回從主要區域讀取。

在設計應用程式以使用 RA-GRS 或 RA-GZRS 進行可用性和復原時,請記住這些重要考慮:

  • 您在主要區域中儲存之數據的唯讀複本會以異步方式複寫在次要區域中。 這個異步複寫表示次要區域中的只讀複本最終與主要區域中的數據 一致 。 記憶體服務會決定次要區域的位置。

  • 您可以使用 Azure 記憶體用戶端連結庫,針對主要區域端點執行讀取和更新要求。 如果主要區域無法使用,您可以自動將讀取要求重新導向至次要區域。 您也可以將應用程式設定為視需要將讀取要求直接傳送至次要區域,即使主要區域可用也一樣。

  • 如果主要區域變得無法使用,您可以啟動帳戶故障轉移機制。 當發生故障轉移到次要區域時,指向主要區域的 DNS 記錄會更改為指向次要區域。 故障轉移完成後,GRS 和 RA-GRS 帳戶會恢復寫入權限。 如需詳細資訊,請參閱災害復原與儲存帳戶異動

使用最終一致的數據

建議的解決方案假設可以接受將可能過時的數據傳回給呼叫的應用程式。 因為次要區域中的數據最終會保持一致,所以在更新次要區域完成複寫之前,主要區域可能會變得無法存取。

例如,假設您的客戶成功提交了更新,但在更新尚未傳播至次要區域時,主要區域發生故障。 當客戶要求將數據讀回時,他們會從次要區域接收過時的數據,而不是更新的數據。 設計應用程式時,您必須決定此行為是否可接受。 如果是,您也需要考慮如何通知使用者。

本文稍後,您將深入瞭解 處理最終一致的數據 ,以及如何檢查 上次同步時間 屬性,以評估主要和次要區域中數據之間的任何差異。

分別處理服務或整體處理服務

雖然不太可能,但一個服務(Blob、佇列、數據表或檔案)可能變得無法使用,而其他服務仍可完全運作。 您可以個別處理每個服務的重試,也可以一般處理所有記憶體服務的重試。

例如,如果您在應用程式中使用佇列和 Blob,您可能會決定放入個別的程式代碼來處理每個服務的可重試錯誤。 如此一來,Blob 服務錯誤只會影響處理 Blob 的應用程式部分,讓佇列繼續正常執行。 不過,如果您決定一起處理所有記憶體服務重試,則如果任一服務傳回可重試的錯誤,對 Blob 和佇列服務的要求將會受到影響。

最終,此決策取決於應用程式的複雜性。 您可能偏好依服務處理失敗,以限制重試的影響。 或者,當您偵測到主要區域中任何記憶體服務發生問題時,您可以決定將所有記憶體服務的讀取要求重新導向至次要區域。

以唯讀模式執行您的應用程式

若要有效地準備主要區域中的中斷,您的應用程式必須能夠同時處理失敗的讀取要求和失敗的更新要求。 如果主要區域失敗,則可以將讀取要求重新導向至次要區域。 不過,無法重新導向更新要求,因為次要區域中的復寫數據是只讀的。 基於這個理由,您必須設計應用程式,才能以唯讀模式執行。

例如,您可以設定一個旗標,在提交任何更新要求至 Azure 儲存體之前進行檢查。 當更新要求通過時,您可以略過要求,並將適當的回應傳回給使用者。 您甚至可以選擇完全停用某些功能,直到問題解決為止,並通知使用者這些功能暫時無法使用。

如果您決定個別處理每個服務的錯誤,您也必須處理以唯讀模式依服務執行應用程式的能力。 例如,您可以為每個服務設定唯讀旗標。 然後,您可以視需要在程式碼中啟用或停用旗標。

能夠以唯讀模式執行應用程式,也可讓您確保主要應用程式升級期間的功能有限。 您可以觸發應用程式以唯讀模式執行,並指向次要數據中心,以確保在進行升級時,沒有人存取主要區域中的數據。

在唯讀模式中執行時處理更新

在唯讀模式中執行時,有許多方式可以處理更新要求。 本節著重於一些要考慮的一般模式。

  • 您可以回應使用者,並通知他們目前未處理更新要求。 例如,聯繫人管理系統可讓使用者存取連絡資訊,但無法進行更新。

  • 您可以將更新佇列至另一個地區。 在此情況下,您會將擱置中的更新要求寫入不同區域中的佇列,然後在主要數據中心再次上線之後處理這些要求。 在此案例中,您應該讓使用者知道更新要求已排入佇列以供稍後處理。

  • 您可以將更新寫入另一個區域中的記憶體帳戶。 當主要區域重新上線時,您可以根據數據的結構,將這些更新合併到主要數據中。 例如,如果您要在名稱中建立具有日期/時間戳的個別檔案,您可以將這些檔案複製到主要區域。 此解決方案可以套用至記錄和IoT資料等工作負載。

處理重試

與雲端中執行之服務通訊的應用程式,必須區分可能發生的未計劃事件和錯誤。 這些錯誤可以是暫時性或持續性的,從暫時中斷連線到因自然災害而中斷。 請務必使用適當的重試處理來設計雲端應用程式,以最大化可用性並改善整體應用程式穩定性。

讀取請求

如果主要區域變成無法使用,讀取要求可以重新導向至次要記憶體。 如先前所述,您的應用程式必須能夠接受,才能讀取過時的數據。 Azure 記憶體用戶端連結庫提供處理重試和將讀取要求重新導向至次要區域的選項。

在此範例中,Blob 記憶體的重試處理是在 類別中設定, BlobClientOptions 而且會套用至 BlobServiceClient 我們使用這些組態選項所建立的物件。 此設定是 主要和次要 方法,其中從主要區域讀取要求重試會重新導向至次要區域。 當主要區域中的失敗預期為暫時時,此方法是最佳方法。

string accountName = "<YOURSTORAGEACCOUNTNAME>";
Uri primaryAccountUri = new Uri($"https://{accountName}.blob.core.windows.net/");
Uri secondaryAccountUri = new Uri($"https://{accountName}-secondary.blob.core.windows.net/");

// Provide the client configuration options for connecting to Azure Blob storage
BlobClientOptions blobClientOptions = new BlobClientOptions()
{
    Retry = {
        // The delay between retry attempts for a fixed approach or the delay
        // on which to base calculations for a backoff-based approach
        Delay = TimeSpan.FromSeconds(2),

        // The maximum number of retry attempts before giving up
        MaxRetries = 5,

        // The approach to use for calculating retry delays
        Mode = RetryMode.Exponential,

        // The maximum permissible delay between retry attempts
        MaxDelay = TimeSpan.FromSeconds(10)
    },

    // If the GeoRedundantSecondaryUri property is set, the secondary Uri will be used for 
    // GET or HEAD requests during retries.
    // If the status of the response from the secondary Uri is a 404, then subsequent retries
    // for the request will not use the secondary Uri again, as this indicates that the resource 
    // may not have propagated there yet.
    // Otherwise, subsequent retries will alternate back and forth between primary and secondary Uri.
    GeoRedundantSecondaryUri = secondaryAccountUri
};

// Create a BlobServiceClient object using the configuration options above
BlobServiceClient blobServiceClient = new BlobServiceClient(primaryAccountUri, new DefaultAzureCredential(), blobClientOptions);

如果您判斷主要區域可能長時間無法使用,您可以設定所有讀取要求以指向次要區域。 此設定只是 次要 方法。 如先前所述,您將需要一個策略來處理在此期間的更新要求,以及通知用戶只處理讀取要求的方式。 在此範例中,我們會建立使用次要區域端點的新實例 BlobServiceClient

string accountName = "<YOURSTORAGEACCOUNTNAME>";
Uri primaryAccountUri = new Uri($"https://{accountName}.blob.core.windows.net/");
Uri secondaryAccountUri = new Uri($"https://{accountName}-secondary.blob.core.windows.net/");

// Create a BlobServiceClient object pointed at the secondary Uri
// Use blobServiceClientSecondary only when issuing read requests, as secondary storage is read-only
BlobServiceClient blobServiceClientSecondary = new BlobServiceClient(secondaryAccountUri, new DefaultAzureCredential(), blobClientOptions);

知道何時切換到只讀模式和 次要要求 是稱為 斷路器模式的架構設計模式的一部分,稍後將討論。

更新請求

更新要求無法重新導向至唯讀的次要記憶體。 如先前所述,當主要區域無法使用時,您的應用程式必須能夠 處理更新要求

斷路器模式也可以套用至更新要求。 若要處理更新要求錯誤,您可以在程式代碼中設定閾值,例如10個連續失敗,並追蹤主要區域的要求失敗數目。 符合閾值之後,您可以將應用程式切換為唯讀模式,以便不再發出對主要區域的更新要求。

如何實作斷路器模式

處理可能需要一段時間才能復原的失敗,是稱為 斷路器模式之架構設計模式的一部分。 此模式的適當實作可防止應用程式重複嘗試執行可能失敗的作業,進而改善應用程式穩定性和復原能力。

斷路器模式的一個層面是識別主要端點發生持續問題時。 若要進行此判斷,您可以監視用戶端遇到可重試錯誤的頻率。 因為每個案例都不同,因此您必須判斷適當的臨界值,以便決定切換至次要端點,並以唯讀模式執行應用程式。

例如,如果主要區域中發生 10 個連續失敗,您可以決定執行 參數。 您可以藉由保留程式代碼中的失敗計數來追蹤此情況。 如果在達到閾值之前成功,請將計數設回零。 如果計數達到臨界值,請切換應用程式以使用次要區域進行讀取要求。

作為替代方法,您可以決定在應用程式中實作自定義監視元件。 此元件可以使用簡單的讀取要求,持續 Ping 主要記憶體端點(例如讀取小型 Blob),以判斷其健康情況。 此方法會佔用一些資源,但不會佔用大量資源。 發現達到閾值的問題時,您會切換到 僅限次要 的讀取要求和只讀模式。 在此案例中,當 Ping 主要記憶體端點再次成功時,您可以切換回主要區域並繼續允許更新。

用來判斷切換切換時機的錯誤臨界值可能會因應用程式內的服務而異,因此您應該考慮設定參數。

另一個考慮是如何處理應用程式的多個實例,以及當您偵測到每個實例中可重試的錯誤時該怎麼辦。 例如,您可能有 20 部 VM 執行,且已載入相同的應用程式。 您是否分別處理每個實例? 如果某個實例開始發生問題,您要只將回應限制為該一個實例嗎? 或者,當某個實例發生問題時,您希望所有實例都以相同方式回應? 個別處理實例比嘗試跨實例協調回應要簡單得多,但您的方法將取決於應用程式的架構。

處理最終一致的數據

異地備援記憶體的運作方式是將交易從主要區域復寫到次要區域。 複寫程式可確保次要區域中的數據最終一致。 這表示主要區域中的所有交易最終都會出現在次要區域中,但出現之前可能會有延遲。 也不能保證交易會以與原本在主要區域中套用相同的順序抵達次要區域。 如果您的交易未按順序抵達次要區域,您可能會 將次要區域中的數據視為處於不一致的狀態,直到服務趕上為止。

下列 Azure 資料表記憶體範例會顯示當您更新員工的詳細數據,使其成為 系統管理員角色的成員時,可能發生的情況。 為了此範例,這需要您更新 員工 實體,並更新含有系統管理員總數計數的 管理者角色 實體。 注意次要區域中的更新是如何不按照順序套用的。

時間 交易 複製 上次同步時間 結果
T0 交易 A:
新增員工
主要中的實體
交易 A 已插入至主伺服器。
尚未複製。
T1 交易 A
複寫至
次要的
T1 交易 A 已複製到次要系統。
最後一次同步時間已更新。
T2 交易 B:
更新
員工單位
在小學階段
T1 交易 B 寫入主要資料庫。
尚未複製。
T3 交易 C:
更新
管理員
中的角色實體
主要
T1 將交易 C 寫入主要存儲區,
尚未複製。
T4 交易 C
複寫至
次要的
T1 交易 C 已經被複寫到次要系統。
LastSyncTime 未更新,因為
交易 B 尚未復寫。
T5 讀取實體
從次要
T1 您取得了過時的員工資料
實體,因為交易 B 尚未完成
尚未複製。 您獲得的新值
系統管理員角色實體,因為 C 具有
複製。 上次同步時間仍未完成
已因交易 B 而更新
尚未複製。 您可以告訴
管理員角色實體不一致
因為實體日期/時間在之後
上次同步時間
T6 交易 B
複寫至
次要的
T6 T6 – 透過 C 的所有交易都有...
已複製,上次同步時間
已更新。

在此範例中,假設客戶端在 T5 時切換至從次要區域讀取。 它目前可以成功讀取 系統管理員角色 實體,但實體包含的系統管理員計數值,與目前標示為次要區域中系統管理員 的員工實體數目 不一致。 您的用戶端可能會顯示此值,且資訊不一致的風險。 或者,用戶端可能會嘗試判斷 系統管理員角色 是否處於潛在的不一致狀態,因為更新已經發生的順序錯亂,然後通知使用者此事實。

若要判斷記憶體帳戶是否有潛在的不一致數據,用戶端可以檢查 上次同步時間 屬性的值。 上次同步處理時間 會告訴您次要區域中的數據上次保持一致的時間,以及服務在該時間點之前套用所有交易的時間。 在上述範例中,服務在次要區域中插入 員工 實體之後,最後一次同步時間會設定為 T1。 當服務設定為 T6 時,它會維持在 T1 上,直到服務更新次要區域中的員工實體為止。 如果用戶端在 T5 讀取實體時擷取上次同步處理時間,則可以將它與實體上的時間戳進行比較。 如果實體上的時間戳晚於上次同步處理時間,則實體的狀態可能不一致,而且您可以採取適當的動作。 使用此欄位需要您知道何時完成主資料庫的最後一次更新。

若要瞭解如何檢查上次同步時間,請參閱 檢查記憶體帳戶的上次同步時間屬性

測試

請務必測試應用程式在遇到可重試的錯誤時是否如預期般運作。 例如,您必須測試應用程式在偵測到問題時切換至次要區域,然後在主要區域再次可用時切換回 。 若要正確測試此行為,您需要一種方法來模擬可重試的錯誤,並控制其發生頻率。

其中一個選項是使用 Fiddler 來攔截和修改腳本中的 HTTP 回應。 此腳本可以識別來自主要端點的回應,並將 HTTP 狀態代碼變更為記憶體用戶端連結庫可辨識為可重試的錯誤。 此代碼段顯示 Fiddler 腳本的簡單範例,該腳本會攔截針對 employeedata 數據表讀取要求的回應,以傳回 502 狀態:

static function OnBeforeResponse(oSession: Session) {
    ...
    if ((oSession.hostname == "\[YOURSTORAGEACCOUNTNAME\].table.core.windows.net")
      && (oSession.PathAndQuery.StartsWith("/employeedata?$filter"))) {
        oSession.responseCode = 502;
    }
}

您可以擴充此範例來攔截更廣泛的要求範圍,並只變更其中一些要求的 responseCode ,以更好地模擬真實世界的案例。 如需自訂 Fiddler 腳本的詳細資訊,請參閱 Fiddler 文件中的 修改要求或回應

如果您已設定可設定的臨界值,以將應用程式切換為唯讀,使用非生產事務磁碟區測試行為會比較容易。


後續步驟

如需完整範例示範如何在主要和次要端點之間來回切換,請參閱 Azure 範例 – 使用斷路器模式與 RA-GRS 儲存