分享方式:


伺服器正常關機

當 Microsoft Azure SignalR Service 設定為預設模式,而由 Azure SignalR Service 充當 SignalR 用戶端與 SignalR 中樞伺服器之間的 Proxy 時,Azure SignalR Service 提供兩種模式來正常關閉 SignalR 中樞伺服器。

使用此功能的主要優點是避免客戶遇到意外的連線中斷。

相反地,您可以根據商務邏輯,等候用戶端連線自行關閉,或甚至將用戶端連線移轉至另一部伺服器,而不會遺失資料。

運作方式

一般而言,正常關機程序有四個階段:

  1. 將伺服器設定為離線

    這表示不會再有用戶端連線路由至此伺服器。

  2. 觸發 OnShutdown 勾點

    您可以為您在伺服器中擁有的每個中樞註冊關機勾點。 在我們收到 Azure SignalR Service 的 FINACK 回應之後,即 Azure SignalR Service 中已將此伺服器設定為離線,將會依註冊順序立即呼叫勾點。

    在這個階段,您可廣播訊息或執行一些清除作業,一旦執行所有關機勾點,我們就會繼續進行下一個階段。

  3. 等候所有用戶端連線完成,視您選擇的模式而定,可能是:

    模式設定為 WaitForClientsToClose

    Azure SignalR Service 會保留現有的用戶端。

    您可能必須設計一種方式,例如將關閉訊息廣播給所有用戶端,然後讓用戶端決定何時關閉/重新連線本身。

    請參閱 ChatSample 使用方式範例,其中我們在關機勾點中廣播 'exit' 訊息以觸發用戶端關閉。

    模式設定為 MigrateClients

    Azure SignalR Service 會嘗試將此伺服器上的用戶端連線重新路由至另一個有效的伺服器。

    在此情況下,由於在 Context 中設定 IConnectionMigrationFeature (可用來識別是否正在移入或移出用戶端連線),將會分別在新伺服器和舊伺服器上觸發 OnConnectedAsyncOnDisconnectedAsync。這項功能在具狀態情況下特別有用。

    傳遞目前的訊息之後會立即移轉用戶端連線,這表示下一個訊息會路由至新的伺服器。

  4. 停止伺服器連線

    在關閉/移轉所有用戶端連線,或超過逾時 (預設 30 秒) 之後,

    SignalR Server SDK 會進行此階段的關機程序,並關閉所有伺服器連線。

    如果用戶端連線無法關閉/移轉,仍然會中斷。 例如,尚未完成適當的目標伺服器/目前的用戶端對伺服器訊息。

範例程式碼。

使用 AddAzureSignalR 新增下列選項:

services.AddSignalR().AddAzureSignalR(option =>
{
    option.GracefulShutdown.Mode = GracefulShutdownMode.WaitForClientsClose;
    // option.GracefulShutdown.Mode = GracefulShutdownMode.MigrateClients;
    option.GracefulShutdown.Timeout = TimeSpan.FromSeconds(30);

    option.GracefulShutdown.Add<Chat>(async (c) =>
    {
        await c.Clients.All.SendAsync("exit");
    });
});

將正常關機模式設定為 MigrateClients 時,設定 OnConnectedOnDisconnected

我們引進了 "IConnectionMigrationFeature",以指出是否正在移入/移出連線。

public class Chat : Hub {

    public override async Task OnConnectedAsync()
    {
        Console.WriteLine($"{Context.ConnectionId} connected.");

        var feature = Context.Features.Get<IConnectionMigrationFeature>();
        if (feature != null)
        {
            Console.WriteLine($"[{feature.MigrateTo}] {Context.ConnectionId} is migrated from {feature.MigrateFrom}.");
            // Your business logic.
        }

        await base.OnConnectedAsync();
    }

    public override async Task OnDisconnectedAsync(Exception e)
    {
        Console.WriteLine($"{Context.ConnectionId} disconnected.");

        var feature = Context.Features.Get<IConnectionMigrationFeature>();
        if (feature != null)
        {
            Console.WriteLine($"[{feature.MigrateFrom}] {Context.ConnectionId} will be migrated to {feature.MigrateTo}.");
            // Your business logic.
        }

        await base.OnDisconnectedAsync(e);
    }
}