分享方式:


Azure SignalR Service 中的復原和災害復原

復原和災害復原是線上系統的常見需求。 Azure SignalR Service 提供 99.9% 的可用性,但仍是一項區域性服務。 當發生全區域服務中斷時,您的服務執行個體不會容錯移轉至另一個區域,因為其一律在一個區域中執行。

針對區域性災害復原,我們建議使用下列兩種方法:

  • 啟用異地複寫 (簡單方式)。 此功能會自動為您處理區域性容錯移轉。 啟用後,只有一個 Azure SignalR 執行個體,而且不會引入任何程式碼變更。 如需詳細資訊,請參閱異地複寫
  • 在服務 SDK 中利用多個端點。 我們服務 SDK 可支援多個 SignalR 服務執行個體,而且會在其中一些執行個體無法使用時,自動切換至其他執行個體。 利用此功能,在災害發生時,您就能夠進行復原,但您必須自行設定正確的系統拓撲。 您將在本文件中了解如何這麼做。

SignalR 服務的高可用性架構

為了確保 SignalR 服務能跨區域進行復原,您需要在不同區域中設定多個服務執行個體。 這樣一來,當某個區域的服務中斷時,其他區域就可用來當做備份。 將應用程式伺服器連線到多個服務執行個體時會有兩個角色:主要和次要。 主要角色是負責接收線上流量的執行個體,而次要角色則作為可完全運作的後援執行個體。 在我們的 SDK 實作中,交涉只會傳回主要端點,因此用戶端只能連線至主要端點。 但當主要執行個體無法運作時,交涉會傳回次要端點,因此,用戶端仍然可以進行連線。 主要執行個體和應用程式伺服器會透過一般伺服器連線來連接,但次要執行個體和應用程式伺服器會透過一種稱為弱連線的特殊連線來連接。 不佳連線的其中一個明顯特性是因次要執行個體位於其他區域而無法接受用戶端連線路由。 將用戶端路由到另一個區域並非最佳選擇 (會增加延遲)。

連線至多個應用程式伺服器時,一個服務執行個體可以有不同的角色。 針對區域案例,其中一個標準設定是有兩組 (含) 以上的 SignalR 服務執行個體和應用程式伺服器。 在每一組配對中,應用程式伺服器與 SignalR 服務會位在相同區域中,而 SignalR 服務會連線到作為主要角色的應用程式伺服器。 每一組的應用程式伺服器和 SignalR 服務之間也會連線,但當 SignalR 連線到另一個區域中的伺服器時,就會變成次要角色。

在此拓撲中,來自一部伺服器的訊息仍然可以傳遞至所有用戶端,因為所有應用程式伺服器和 SignalR 服務執行個體都會互相連線。 但是,連線的用戶端會路由至相同區域中的應用程式伺服器,以達到最佳的網路延遲。

下列圖表說明此類拓撲:

此圖表顯示兩個區域,每個區域都有一個應用程式伺服器和一個 SignalR Service,其中每個伺服器都會與該區域中的 SignalR Service 建立關聯,作為主要伺服器,並與另一個區域中的服務相關聯,作為次要伺服器。

設定數個 SignalR Service 服務執行個體

應用程式伺服器和 Azure Functions 都支援數個 SignalR Service 服務執行個體。

在每個區域中建立 SignalR 服務和應用程式伺服器/Azure Functions 之後,您可以將應用程式伺服器/Azure Functions 設定為連線至所有 SignalR Service 服務執行個體。

透過 config

您應該已知道如何透過環境變數/應用程式設定/web.cofig 來設定 SignalR Service 服務連接字串,亦即透過名為 Azure:SignalR:ConnectionString config 的項目。 如果您有多個端點,您可以使用下列格式在多個 config 項目中設定每個連接字串:

Azure:SignalR:ConnectionString:<name>:<role>

在 ConnectionString 中,<name> 是端點名稱,而 <role> 則是其角色 (主要或次要)。 名稱為選擇性項目,但此項目有助於您進一步自訂多個端點之間的路由行為。

透過程式碼

如果您想要將連接字串儲存於別處,您也可以在程式碼中讀取該連接字串,並在呼叫 AddAzureSignalR() (使用 ASP.NET Core) 或 MapAzureSignalR() (使用 ASP.NET) 時,將這些連接字串當作參數使用。

以下是範例程式碼:

ASP.NET Core:

services.AddSignalR()
        .AddAzureSignalR(options => options.Endpoints = new ServiceEndpoint[]
        {
            new ServiceEndpoint("<connection_string1>", EndpointType.Primary, "region1"),
            new ServiceEndpoint("<connection_string2>", EndpointType.Secondary, "region2"),
        });

ASP.NET:

app.MapAzureSignalR(GetType().FullName, hub,  options => options.Endpoints = new ServiceEndpoint[]
    {
        new ServiceEndpoint("<connection_string1>", EndpointType.Primary, "region1"),
        new ServiceEndpoint("<connection_string2>", EndpointType.Secondary, "region2"),
    };

您可以設定數個主要或次要執行個體。 如果有多個主要和/或次要執行個體,交涉會依照下列順序傳回端點:

  1. 如果線上至少有一個主要執行個體,請傳回隨機的主要線上執行個體。
  2. 如果所有主要執行個體都關閉,則會傳回隨機的次要線上執行個體。

針對 Azure Functions SignalR 繫結

若要啟用多個 SignalR Service 執行個體,您應:

  1. 使用 Persistent 傳輸類型。

    預設傳輸類型為 Transient 模式。 您應將下列項目新增至 local.settings.json 檔案或 Azure 上的應用程式設定。

    {
        "AzureSignalRServiceTransportType":"Persistent"
    }
    

    注意

    Transient 模式切換為 Persistent 模式時,可能會出現 JSON 序列化行為變更,因為在 Transient 模式下,會使用 Newtonsoft.Json 程式庫來序列化中樞方法的引數,但在 Persistent 模式下依預設會使用 System.Text.Json 程式庫。 System.Text.JsonNewtonsoft.Json 的預設行為有一些重要差異。 如果您想要在 Persistent 模式下使用 Newtonsoft.Json,您可以在 local.settings.json 檔案或 Azure__SignalR__HubProtocol=NewtonsoftJson Azure 入口網站上新增設定項目 "Azure:SignalR:HubProtocol":"NewtonsoftJson"

  2. 在您的設定中設定多個 SignalR Service 端點項目。

    我們使用 ServiceEndpoint 物件來表示 SignalR Service 執行個體。 您可以使用項目索引鍵中的 <EndpointName><EndpointType> 以及項目值中的連接字串來定義服務端點。 索引鍵的格式如下:

    Azure:SignalR:Endpoints:<EndpointName>:<EndpointType>
    

    <EndpointType> 是選擇性的,預設為 primary。 請參閱下方的範例:

    {
        "Azure:SignalR:Endpoints:EastUs":"<ConnectionString>",
    
        "Azure:SignalR:Endpoints:EastUs2:Secondary":"<ConnectionString>",
    
        "Azure:SignalR:Endpoints:WestUs:Primary":"<ConnectionString>"
    }
    

    注意

    • 當您在 Azure 入口網站上的 App Service 中設定 Azure SignalR 端點時,別忘了將 ":" 取代為 "__" (索引鍵中的雙底線)。 若要了解原因,請參閱環境變數

    • 使用索引鍵 {ConnectionStringSetting} (預設為 "AzureSignalRConnectionString") 設定的連接字串,也會識別為具有空名稱的主要服務端點。 不過,對於不建議將此設定樣式用於多個端點。

針對管理 SDK

從組態新增多個端點

使用 SignalR Service 連接字串的索引鍵 Azure:SignalR:Endpoints 進行設定。 如果索引鍵的格式應為 Azure:SignalR:Endpoints:{Name}:{EndpointType},其中,NameEndpointTypeServiceEndpoint 物件的屬性,且可從程式碼存取。

您可以使用下列 dotnet 命令新增多個執行個體連接字串:

dotnet user-secrets set Azure:SignalR:Endpoints:east-region-a <ConnectionString1>
dotnet user-secrets set Azure:SignalR:Endpoints:east-region-b:primary <ConnectionString2>
dotnet user-secrets set Azure:SignalR:Endpoints:backup:secondary <ConnectionString3>

從程式碼新增多個端點

ServiceEndpoint 類別描述 Azure SignalR Service 端點的屬性。 使用 Azure SignalR 管理 SDK 時,可以透過下列方式設定多個執行個體端點:

var serviceManager = new ServiceManagerBuilder()
                    .WithOptions(option =>
                    {
                        options.Endpoints = new ServiceEndpoint[]
                        {
                            // Note: this is just a demonstration of how to set options.Endpoints
                            // Having ConnectionStrings explicitly set inside the code is not encouraged
                            // You can fetch it from a safe place such as Azure KeyVault
                            new ServiceEndpoint("<ConnectionString0>"),
                            new ServiceEndpoint("<ConnectionString1>", type: EndpointType.Primary, name: "east-region-a"),
                            new ServiceEndpoint("<ConnectionString2>", type: EndpointType.Primary, name: "east-region-b"),
                            new ServiceEndpoint("<ConnectionString3>", type: EndpointType.Secondary, name: "backup"),
                        };
                    })
                    .BuildServiceManager();

容錯移轉順序和最佳做法

現在您已設定正確的系統拓撲。 每當有一個 SignalR 服務執行個體停止運作時,線上流量就會路由至其他執行個體。 以下是主要執行個體停止運作 (並在稍後復原) 時發生的狀況:

  1. 主要服務執行個體停止運作時,此執行個體上的所有伺服器連線皆會卸除。
  2. 連線到此執行個體的所有伺服器都會將其標示為離線,而交涉會停止傳回此端點,並開始傳回次要端點。
  3. 此執行個體上的所有用戶端連線也會關閉,用戶端將會重新連線。 由於應用程式伺服器現在會傳回次要端點,因此用戶端會連線至次要執行個體。
  4. 現在,次要執行個體會接收所有線上流量。 所有來自伺服器的訊息仍可傳遞到用戶端,因為次要執行個體會連線到所有應用程式伺服器。 但是從用戶端傳送到伺服器的訊息只會路由至相同區域中的應用程式伺服器。
  5. 當主要執行個體復原並回到線上狀態之後,應用程式伺服器就會重新與之建立連線,並將其標示為 [線上]。 交涉現在會再次傳回主要端點,讓新的用戶端連線到主要執行個體。 但現有的用戶端不會卸除,而且會繼續路由到次要區域,直到這些用戶端自行中斷為止。

以下圖示說明如何在 SignalR 服務進行容錯移轉:

圖 1 容錯移轉前 容錯移轉之前

圖 2 容錯移轉後 容錯移轉後

圖 3 主要復原後不久 主要執行個體復原後不久

在正常的情況下,您只會看到主要應用程式伺服器及 SignalR 服務有線上流量 (藍色)。 容錯移轉之後,次要應用程式伺服器與 SignalR 服務也會開始作用。 當主要 SignalR 服務恢復上線後,新的用戶端將會連線到主要 SignalR。 但現有的用戶端仍是連線至次要執行個體,因此兩個執行個體都有流量。 在所有現有的用戶端都中斷連線後,系統就會回到正常情況 (圖 1)。

實作跨區域的高可用性架構有兩個主要模式:

  1. 第一個模式是讓一組應用程式伺服器和 SignalR 服務執行個體接收所有線上流量,並將另一組當作備份 (稱為主動/被動,如圖 1 所示)。
  2. 另一個模式是有兩組 (或兩組以上) 應用程式伺服器和 SignalR 服務執行個體,每一組都接收部分線上流量及作為其他組的備份 (稱為主動/主動,類似圖 3)。

SignalR 服務可支援這兩種模式,主要差異在於您如何實作應用程式伺服器。 如果應用程式伺服器為主動/被動,SignalR 服務也會是主動/被動 (因為主要應用程式伺服器只會傳回其主要的 SignalR 服務執行個體)。 如果應用程式伺服器為主動/主動,SignalR 服務也會是主動/主動 (因為所有應用程式伺服器會傳回自己的主要 SignalR 執行個體,因此所有執行個體都可以接收流量)。

請注意,無論您選擇使用哪一個模式,您都必須將每個 SignalR Service 服務執行個體連線至作為主要的應用程式伺服器。

也因為 SignalR 連線的本質 (長時間連線),用戶端會在發生災害和容錯移轉時遇到連線中斷。 您必須在用戶端處理這類情況,讓終端客戶了解此情況。 例如,在連線關閉後重新連線。

如何測試容錯移轉

請遵循下列步驟來觸發容錯移轉:

  1. 在入口網站中主要資源的 [網路] 索引標籤中,停用公用網路存取。 如果資源已啟用私人網路,請使用存取控制規則來拒絕所有流量。
  2. 重新啟動主要資源。

下一步

在本文中,您已經了解如何設定您的應用程式以恢復 SignalR 服務的功能。 若要深入了解 SignalR 服務中的伺服器/用戶端連線及連線路由,您可以閱讀這篇文章,以了解 SignalR 服務的內部運作。

對於分區化等縮放規模的情況,在這些情況下會同時使用數個執行個體來處理大量的連線,請參閱如何縮放數個執行個體

如需瞭解如何使用數個 SignalR Service 服務執行個體來設定 Azure Functions 的詳細資訊,請參閱 Azure Functions 中的多重 Azure SignalR Service 服務執行個體支援