Плавное завершение работы сервера

Служба Microsoft Azure SignalR предоставляет два режима для корректного завершения работы сервера SignalR Hub, когда служба Azure SignalR настроена в режиме по умолчанию, при котором она выступает в качестве прокси-сервера между клиентами SignalR и сервером Центра SignalR.

Ключевым преимуществом использования этой функции является предотвращение неожиданного прерывания подключения клиента.

Вместо этого можно либо дождаться закрытия клиентских подключений в отношении бизнес-логики, либо даже перенести подключение клиента к другому серверу, не теряя данные.

Принцип работы

Как правило, в процессе корректного завершения работы будет четыре этапа:

  1. Переведите сервер в офлайн-режим

    Это означает, что больше клиентских подключений не будет перенаправлено на этот сервер.

  2. Триггерные хуки OnShutdown

    Вы можете зарегистрировать перехватчики завершения работы для каждого концентратора, которым вы владеете на сервере. Они будут вызываться в отношении зарегистрированного заказа сразу после получения ответа FINACK от нашей службы Azure SignalR, что означает, что этот сервер был установлен в автономном режиме в службе Azure SignalR.

    Вы можете транслировать сообщения или выполнять некоторые задания очистки на этом этапе. После выполнения всех перехватчиков завершения работы мы перейдем к следующему этапу.

  3. Дождитесь завершения всех клиентских подключений, зависит от выбранного режима. Это может быть:

    Для режима задано значение WaitForClientsToClose

    Служба Azure SignalR будет поддерживать существующих клиентов.

    Возможно, вам придется разработать способ, например транслировать закрываемое сообщение всем клиентам, а затем позволить клиентам решить, когда закрыть или повторно подключиться.

    Прочитайте ChatSample, где показан пример использования, в котором мы отправляем сообщение 'exit', чтобы инициировать закрытие клиента в перехватчике завершения работы.

    Режим установлен на MigrateClients

    Служба Azure SignalR попытается перенаправить подключение клиента на этом сервере к другому допустимому серверу.

    В этом сценарии OnConnectedAsync и OnDisconnectedAsync будут активированы на новом сервере и старом сервере соответственно с установкой IConnectionMigrationFeature в Context, которую можно использовать для определения того, переносится ли клиентское подключение на сервер или с сервера. Эта функция может быть полезна особенно для сценариев с сохранением состояния.

    Подключение клиента будет немедленно перенесено после доставки текущего сообщения, что означает, что следующее сообщение будет перенаправлено на новый сервер.

  4. Остановка подключений к серверу

    После закрытия и переноса всех клиентских подключений или превышения времени ожидания (30-х по умолчанию)

    Пакет SDK для сервера SignalR продолжит процесс завершения работы на этом этапе и закройте все подключения к серверу.

    Клиентские подключения по-прежнему будут удалены, если не удалось закрыть или перенести. Например, нет подходящего целевого сервера, или передача текущего сообщения "клиент — сервер" не завершена.

Примеры кодов.

Добавьте следующие опции 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");
    });
});

настройте OnConnected и OnDisconnected, включая режим корректного завершения работы MigrateClients.

Мы ввели "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);
    }
}