サーバーのグレースフル シャットダウン
Microsoft Azure SignalR Service では、Azure SignalR Service が既定モードとして構成され、SignalR クライアントと SignalR ハブ サーバーの間のプロキシとして機能する場合に、SignalR ハブ サーバーを正常にシャットダウンするための 2 つのモードが提供されています。
この機能を利用する主要な利点は、予想外の接続の切断を顧客に体験させないことにあります。
その代わり、ビジネス ロジックとの関連でクライアント接続が自動的に閉じられるのを待ちます。あるいは、データを失うことなく、別のサーバーにクライアント接続を移行することもできます。
動作方法
通常、グレースフル シャットダウン プロセスには、次の 4 つの段階があります。
サーバーをオフラインにする
このサーバーにルート指定されるクライアント接続がなくなることを意味します。
OnShutdown
フックをトリガーするサーバーに所有しているハブごとにシャットダウン フックを登録できます。 それらは、Azure SignalR Service でこのサーバーがオフラインに設定されたことを意味する FINACK 応答が Azure SignalR Service から届いた直後に、登録されている順序で呼び出されます。
この段階では、メッセージの配信や消去作業などを実行できます。すべてのシャットダウン フックが実行されると、次の段階に進みます。
すべてのクライアント接続が完了するまで待つ。選択したモードに依存します。次があります。
WaitForClientsToClose に設定されたモード
Azure SignalR Service には既存のクライアントが保持されます。
場合によっては、終了メッセージをすべてのクライアントに配信するなど、手段を設計し、自動的に閉じるか再接続するタイミングをクライアントに決定させる必要があります。
使用例については ChatSample をお読みください。このサンプルでは、シャットダウン フックでクライアント終了をトリガーする "exit" メッセージを配信します。
MigrateClients に設定されたモード
Azure SignalR Service では、このサーバーでのクライアント接続のルート指定を別の有効なサーバーに変更することが試行されます。
このシナリオでは、
OnConnectedAsync
とOnDisconnectedAsync
が新しいサーバーと古いサーバーでそれぞれトリガーされます。IConnectionMigrationFeature
がContext
に設定されていますが、これはクライアント接続が移行で入ってきたのか、出て行ったのかを特定する目的で利用できます。この機能は、ステートフル シナリオで特に便利です。クライアント接続は、現在のメッセージが配信された直後に、つまり、次のメッセージが新しいサーバーにルート指定されるタイミングで移行されます。
サーバー接続を停止する
すべてのクライアント接続が閉じられるか、移行されると、あるいはタイムアウト時間 (既定で 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
に設定されているとき、OnConnected
と OnDisconnected
を構成します。
接続が移行で入ってきたのか、出て行ったのかを示す "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);
}
}