Skalowanie usługi SignalR Service z wieloma wystąpieniami

Zestaw SDK usługi SignalR Service obsługuje wiele punktów końcowych dla wystąpień usługi SignalR Service. Za pomocą tej funkcji można skalować połączenia współbieżne lub używać jej do obsługi komunikatów między regionami.

Dla ASP.NET Core

Dodawanie wielu punktów końcowych z konfiguracji

Skonfiguruj przy użyciu klucza Azure:SignalR:ConnectionString lub Azure:SignalR:ConnectionString: parametry połączenia usługi SignalR Service.

Jeśli klucz zaczyna się od Azure:SignalR:ConnectionString:, powinien mieć format Azure:SignalR:ConnectionString:{Name}:{EndpointType}, gdzie Name i EndpointType są właściwościami ServiceEndpoint obiektu i są dostępne z kodu.

Możesz dodać wiele parametry połączenia wystąpień przy użyciu następujących dotnet poleceń:

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

Dodawanie wielu punktów końcowych z kodu

Klasa ServiceEndpoint opisuje właściwości punktu końcowego usługi Azure SignalR Service. Możesz skonfigurować wiele punktów końcowych wystąpienia podczas korzystania z zestawu SDK usługi Azure SignalR Service za pomocą:

services.AddSignalR()
        .AddAzureSignalR(options => 
        {
            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"),
            };
        });

Dostosowywanie routera punktu końcowego

Domyślnie zestaw SDK używa elementu DefaultEndpointRouter do odbierania punktów końcowych.

Zachowanie domyślne

  1. Routing żądań klienta:

    Gdy klient /negotiate z serwerem aplikacji. Domyślnie zestaw SDK losowo wybiera jeden punkt końcowy z zestawu dostępnych punktów końcowych usługi.

  2. Routing komunikatów serwera:

    Podczas wysyłania komunikatu do określonego połączenia , a połączenie docelowe jest kierowane do bieżącego serwera, komunikat przechodzi bezpośrednio do tego połączonego punktu końcowego. W przeciwnym razie komunikaty są emitowane do każdego punktu końcowego usługi Azure SignalR.

Dostosowywanie algorytmu routingu

Możesz utworzyć własny router, gdy masz specjalną wiedzę, aby określić, do których punktów końcowych powinny przejść komunikaty.

W poniższym przykładzie zdefiniowano router niestandardowy, który kieruje komunikaty grupą rozpoczynającą east- się od punktu końcowego o nazwie east:

private class CustomRouter : EndpointRouterDecorator
{
    public override IEnumerable<ServiceEndpoint> GetEndpointsForGroup(string groupName, IEnumerable<ServiceEndpoint> endpoints)
    {
        // Override the group broadcast behavior, if the group name starts with "east-", only send messages to endpoints inside east
        if (groupName.StartsWith("east-"))
        {
            return endpoints.Where(e => e.Name.StartsWith("east-"));
        }

        return base.GetEndpointsForGroup(groupName, endpoints);
    }
}

Poniższy przykład zastępuje domyślne zachowanie negocjowania i wybiera punkt końcowy w zależności od lokalizacji serwera aplikacji.

private class CustomRouter : EndpointRouterDecorator
{    public override ServiceEndpoint GetNegotiateEndpoint(HttpContext context, IEnumerable<ServiceEndpoint> endpoints)
    {
        // Override the negotiate behavior to get the endpoint from query string
        var endpointName = context.Request.Query["endpoint"];
        if (endpointName.Count == 0)
        {
            context.Response.StatusCode = 400;
            var response = Encoding.UTF8.GetBytes("Invalid request");
            context.Response.Body.Write(response, 0, response.Length);
            return null;
        }

        return endpoints.FirstOrDefault(s => s.Name == endpointName && s.Online) // Get the endpoint with name matching the incoming request
               ?? base.GetNegotiateEndpoint(context, endpoints); // Or fallback to the default behavior to randomly select one from primary endpoints, or fallback to secondary when no primary ones are online
    }
}

Nie zapomnij zarejestrować routera w kontenerze DI przy użyciu:

services.AddSingleton(typeof(IEndpointRouter), typeof(CustomRouter));
services.AddSignalR()
        .AddAzureSignalR(
            options => 
            {
                options.Endpoints = new ServiceEndpoint[]
                {
                    new ServiceEndpoint(name: "east", connectionString: "<connectionString1>"),
                    new ServiceEndpoint(name: "west", connectionString: "<connectionString2>"),
                    new ServiceEndpoint("<connectionString3>")
                };
            });

W przypadku ASP.NET

Dodawanie wielu punktów końcowych z konfiguracji

Konfiguracja przy użyciu klucza Azure:SignalR:ConnectionString lub Azure:SignalR:ConnectionString: parametry połączenia usługi SignalR Service.

Jeśli klucz zaczyna się od Azure:SignalR:ConnectionString:, powinien mieć format Azure:SignalR:ConnectionString:{Name}:{EndpointType}, gdzie Name i EndpointType są właściwościami ServiceEndpoint obiektu i są dostępne z kodu.

Do pliku można dodać wiele parametry połączenia wystąpień:web.config

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  <connectionStrings>
    <add name="Azure:SignalR:ConnectionString" connectionString="<ConnectionString1>"/>
    <add name="Azure:SignalR:ConnectionString:en-us" connectionString="<ConnectionString2>"/>
    <add name="Azure:SignalR:ConnectionString:zh-cn:secondary" connectionString="<ConnectionString3>"/>
    <add name="Azure:SignalR:ConnectionString:Backup:secondary" connectionString="<ConnectionString4>"/>
  </connectionStrings>
  ...
</configuration>

Dodawanie wielu punktów końcowych z kodu

Klasa ServiceEndpoint opisuje właściwości punktu końcowego usługi Azure SignalR Service. Możesz skonfigurować wiele punktów końcowych wystąpienia podczas korzystania z zestawu SDK usługi Azure SignalR Service za pomocą:

app.MapAzureSignalR(
    this.GetType().FullName, 
    options => {
            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("<ConnectionString1>"),
                new ServiceEndpoint("<ConnectionString2>"),
                new ServiceEndpoint("<ConnectionString3>"),
            }
        });

Dostosowywanie routera

Jedyną różnicą między ASP.NET SignalR i ASP.NET Core SignalR jest typ kontekstu http dla .GetNegotiateEndpoint W przypadku ASP.NET SignalR jest to typ IOwinContext .

Poniższy kod jest niestandardowym przykładem negocjowania dla usługi ASP.NET SignalR:

private class CustomRouter : EndpointRouterDecorator
{
    public override ServiceEndpoint GetNegotiateEndpoint(IOwinContext context, IEnumerable<ServiceEndpoint> endpoints)
    {
        // Override the negotiate behavior to get the endpoint from query string
        var endpointName = context.Request.Query["endpoint"];
        if (string.IsNullOrEmpty(endpointName))
        {
            context.Response.StatusCode = 400;
            context.Response.Write("Invalid request.");
            return null;
        }

        return endpoints.FirstOrDefault(s => s.Name == endpointName && s.Online) // Get the endpoint with name matching the incoming request
               ?? base.GetNegotiateEndpoint(context, endpoints); // Or fallback to the default behavior to randomly select one from primary endpoints, or fallback to secondary when no primary ones are online
    }
}

Nie zapomnij zarejestrować routera w kontenerze DI przy użyciu:

var hub = new HubConfiguration();
var router = new CustomRouter();
hub.Resolver.Register(typeof(IEndpointRouter), () => router);
app.MapAzureSignalR(GetType().FullName, hub, options => {
    options.Endpoints = new ServiceEndpoint[]
                {
                    new ServiceEndpoint(name: "east", connectionString: "<connectionString1>"),
                    new ServiceEndpoint(name: "west", connectionString: "<connectionString2>"),
                    new ServiceEndpoint("<connectionString3>")
                };
});

Metryki punktu końcowego usługi

Aby włączyć zaawansowany router, zestaw SDK serwera SignalR udostępnia wiele metryk, które ułatwiają serwerowi podejmowanie inteligentnych decyzji. Właściwości znajdują się w obszarze ServiceEndpoint.EndpointMetrics.

Nazwa metryki opis
ClientConnectionCount Łączna liczba współbieżnych połączeń klienckich we wszystkich centrach punktu końcowego usługi
ServerConnectionCount Łączna liczba współbieżnych połączeń serwera we wszystkich centrach punktu końcowego usługi
ConnectionCapacity Łączny limit przydziału połączeń dla punktu końcowego usługi, w tym połączenia klienta i serwera

Poniższy kod jest przykładem dostosowywania routera zgodnie z ClientConnectionCount.

private class CustomRouter : EndpointRouterDecorator
{
    public override ServiceEndpoint GetNegotiateEndpoint(HttpContext context, IEnumerable<ServiceEndpoint> endpoints)
    {
        return endpoints.OrderBy(x => x.EndpointMetrics.ClientConnectionCount).FirstOrDefault(x => x.Online) // Get the available endpoint with minimal clients load
               ?? base.GetNegotiateEndpoint(context, endpoints); // Or fallback to the default behavior to randomly select one from primary endpoints, or fallback to secondary when no primary ones are online
    }
}

Punkty usługi skalowania dynamicznego

Z zestawu SDK w wersji 1.5.0 włączamy dynamiczne skalowanie punktów serviceEndpoint dla wersji ASP.NET Core. Dlatego nie musisz ponownie uruchamiać serwera aplikacji, gdy trzeba dodać/usunąć punkt końcowy usługi. Ponieważ platforma ASP.NET Core obsługuje konfigurację domyślną, na przykład appsettings.json w programie reloadOnChange: true, nie musisz zmieniać kodu i jest ona obsługiwana z natury. Jeśli chcesz dodać niestandardową konfigurację i pracować z przeładowywaniem na gorąco, zapoznaj się z tematem Konfiguracja w programie ASP.NET Core.

Uwaga

Biorąc pod uwagę czas konfigurowania połączenia między serwerem/usługą i klientem/usługą, może być inny, aby zapewnić brak utraty komunikatów podczas procesu skalowania, mamy okres przejściowy oczekiwania na przygotowanie połączeń serwera przed otwarciem nowego punktu usługi ServiceEndpoint dla klientów. Zazwyczaj ukończenie tego procesu trwa kilka sekund i będzie można zobaczyć komunikat dziennika, na przykład Succeed in adding endpoint: '{endpoint}' wskazujący ukończenie procesu.

W niektórych oczekiwanych sytuacjach, takich jak problemy z siecią między regionami lub niespójności konfiguracji na różnych serwerach aplikacji, okres przejściowy może nie zakończyć się poprawnie. W takich przypadkach zaleca się ponowne uruchomienie serwera aplikacji, gdy proces skalowania nie działa poprawnie.

Domyślny okres limitu czasu dla skali wynosi 5 minut i można go dostosować, zmieniając wartość w pliku ServiceOptions.ServiceScaleTimeout. Jeśli masz wiele serwerów aplikacji, zaleca się rozszerzenie wartości nieco więcej.

Konfiguracja w scenariuszach obejmujących wiele regionów

Obiekt ServiceEndpoint ma EndpointType właściwość o wartości primary lub secondary.

Podstawowe punkty końcowe są preferowanymi punktami końcowymi do odbierania ruchu klienta, ponieważ mają bardziej niezawodne połączenia sieciowe. Pomocnicze punkty końcowe mają mniej niezawodne połączenia sieciowe i są używane tylko dla serwera do ruchu klienta. Na przykład pomocnicze punkty końcowe są używane do nadawania komunikatów zamiast klienta do ruchu serwera.

W przypadkach obejmujących wiele regionów sieć może być niestabilna. W przypadku serwera aplikacji znajdującego się w regionie Wschodnie stany USA punkt końcowy usługi SignalR Service znajdujący się w tym samym regionie Wschodnie stany USA to primary i punkty końcowe w innych regionach oznaczonych jako secondary. W tej konfiguracji punkty końcowe usługi w innych regionach mogą odbierać komunikaty z tego serwera aplikacji Wschodnie stany USA, ale do tego serwera aplikacji nie są kierowani klienci między regionami. Na poniższym diagramie przedstawiono architekturę:

Infrastruktura geograficzna między obszarami geograficznymi

Gdy klient spróbuje /negotiate z serwerem aplikacji z routerem domyślnym, zestaw SDK losowo wybiera jeden punkt końcowy z zestawu dostępnych primary punktów końcowych. Gdy podstawowy punkt końcowy jest niedostępny, zestaw SDK wybiera losowo wszystkie dostępne secondary punkty końcowe. Punkt końcowy jest oznaczony jako dostępny , gdy połączenie między serwerem a punktem końcowym usługi jest aktywne.

W scenariuszu między regionami, gdy klient spróbuje /negotiate z serwerem aplikacji hostowanym w regionie Wschodnie stany USA, domyślnie zawsze zwraca primary punkt końcowy znajdujący się w tym samym regionie. Gdy wszystkie punkty końcowe Wschodnie stany USA nie są dostępne, router przekierowuje klienta do punktów końcowych w innych regionach. W poniższej sekcji trybu failover opisano szczegółowo scenariusz.

Normalna negocjacja

Tryb failover

Gdy punkt końcowy nie primary jest dostępny, klient /negotiate wybiera z dostępnych secondary punktów końcowych. Ten mechanizm trybu failover wymaga, aby każdy punkt końcowy był primary punktem końcowym co najmniej jednego serwera aplikacji.

Diagram przedstawiający proces mechanizmu trybu failover.

Następne kroki

W scenariuszach wysokiej dostępności i odzyskiwania po awarii można używać wielu punktów końcowych.