Skalierung des SignalR-Diensts mit mehreren Instanzen

Das SignalR Service SDK unterstützt mehrere Endpunkte für SignalR Service-Instanzen. Sie können dieses Feature zum Skalieren der gleichzeitigen Verbindungen oder für das regionsübergreifende Messaging verwenden.

Für ASP.NET Core

Hinzufügen mehrerer Endpunkte aus der Konfiguration

Konfigurieren Mit Schlüssel Azure:SignalR:ConnectionString oder Azure:SignalR:ConnectionString: für SignalR-Dienst-Verbindungszeichenfolge.

Wenn der Schlüssel mit Azure:SignalR:ConnectionString:dem Schlüssel beginnt, sollte er sich im Format Azure:SignalR:ConnectionString:{Name}:{EndpointType}befinden, in dem Name es sich um Eigenschaften des ServiceEndpoint Objekts handelt EndpointType und über Code darauf zugreifen kann.

Sie können Verbindungszeichenfolgen für mehrere Instanzen hinzufügen, indem Sie die folgenden dotnet-Befehle verwenden:

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>

Hinzufügen mehrerer Endpunkte aus Code

Eine ServicEndpoint Klasse beschreibt die Eigenschaften eines Azure SignalR Service-Endpunkts. Sie können mehrere Instanzen für Endpunkte konfigurieren, wenn Sie das Azure SignalR Service SDK verwenden:

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"),
            };
        });

Anpassen des Endpunktrouters

Standardmäßig wird vom SDK das DefaultEndpointRouter-Element verwendet, um Endpunkte zu erfassen.

Standardverhalten

  1. Routing von Clientanforderungen:

    Wird verwendet, wenn Clients die Aushandlung (/negotiate) mit dem App-Server durchführen. Standardmäßig nimmt das SDK eine zufällige Auswahl eines Endpunkts aus den verfügbaren Dienstendpunkten vor.

  2. Servernachrichtenweiterleitung:

    Beim Senden einer Nachricht an eine bestimmte Verbindung und die Zielverbindung wird an den aktuellen Server weitergeleitet, wird die Nachricht direkt an diesen verbundenen Endpunkt weitergeleitet. Andernfalls werden die Nachrichten an jeden Azure SignalR-Endpunkt übertragen.

Anpassen des Routingalgorithmus

Sie können Ihre eigenen Router erstellen, wenn Sie über spezielle Kenntnisse verfügen und ermitteln können, an welche Endpunkte die Nachrichten gesendet werden sollen.

Im folgenden Beispiel wird ein benutzerdefinierter Router definiert, der Nachrichten mit einer Gruppe leitet, beginnend mit east- dem Endpunkt namens 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);
    }
}

Im folgenden Beispiel wird das Standard-Aushandlungsverhalten außer Kraft gesetzt und der Endpunkt abhängig vom Speicherort des App-Servers ausgewählt.

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
    }
}

Vergessen Sie nicht, den Router wie folgt für den DI-Container zu registrieren:

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>")
                };
            });

Für ASP.NET

Hinzufügen mehrerer Endpunkte aus der Konfiguration

Konfiguration mit Schlüssel Azure:SignalR:ConnectionString oder Azure:SignalR:ConnectionString: für signalR Service Verbindungszeichenfolge.

Wenn der Schlüssel mit Azure:SignalR:ConnectionString: beginnt, sollte er das Format Azure:SignalR:ConnectionString:{Name}:{EndpointType} aufweisen. Hierbei sind Name und EndpointType Eigenschaften des ServiceEndpoint-Objekts, die über den Code zugänglich sind.

Sie können web.config Verbindungszeichenfolgen für mehrere Instanzen hinzufügen:

<?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>

Hinzufügen mehrerer Endpunkte aus Code

Eine ServiceEndpoint Klasse beschreibt die Eigenschaften eines Azure SignalR Service-Endpunkts. Sie können mehrere Instanzen für Endpunkte konfigurieren, wenn Sie das Azure SignalR Service SDK verwenden:

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>"),
            }
        });

Anpassen eines Routers

Der einzige Unterschied zwischen ASP.NET SignalR und ASP.NET Core SignalR ist der HTTP-Kontexttyp für GetNegotiateEndpoint. Für ASP.NET SignalR lautet der Typ IOwinContext.

Der folgende Code ist ein benutzerdefiniertes Aushandlungsbeispiel für 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
    }
}

Vergessen Sie nicht, den Router wie folgt für den DI-Container zu registrieren:

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>")
                };
});

Dienstendpunktmetriken

Um einen erweiterten Router zu aktivieren, stellt das SignalR-Server-SDK mehrere Metriken bereit, mit denen der Server intelligente Entscheidungen treffen kann. Die Eigenschaften befinden sich unter ServiceEndpoint.EndpointMetrics.

Metrikname Beschreibung
ClientConnectionCount Gesamtanzahl der gleichzeitigen Clientverbindungen auf allen Hubs für den Dienstendpunkt
ServerConnectionCount Gesamtanzahl der gleichzeitigen Serververbindungen auf allen Hubs für den Dienstendpunkt
ConnectionCapacity Gesamtverbindungskontingent für den Dienstendpunkt, einschließlich Client- und Serververbindungen

Der folgende Code ist ein Beispiel für das Anpassen eines Routers ClientConnectionCountgemäß .

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
    }
}

Dienstendpunkte (ServiceEndpoints) mit dynamischer Skalierung

Ab SDK Version 1.5.0 aktivieren wir zuerst Dienstendpunkte (ServiceEndpoints) mit dynamischer Skalierung für die ASP.NET Core-Version. Daher müssen Sie den App-Server nicht neu starten, wenn Sie ein ServiceEndpoint-Element hinzufügen/entfernen müssen. Da ASP.NET Core die Standardkonfiguration wie appsettings.json mit reloadOnChange: true unterstützt, müssen Sie keinen Code ändern, er wird automatisch unterstützt. Wenn Sie eine benutzerdefinierte Konfiguration hinzufügen und Hot Reload nutzen möchten, lesen Sie Konfiguration in ASP.NET Core.

Hinweis

In Anbetracht der Zeit der Verbindungseinrichtung zwischen Server/Dienst und Client/Dienst kann unterschiedlich sein, um sicherzustellen, dass während des Skalierungsprozesses kein Nachrichtenverlust besteht, haben wir einen Stagingzeitraum, der darauf wartet, dass Serververbindungen bereit sind, bevor der neue ServiceEndpoint für Clients geöffnet wird. In der Regel dauert es Sekunden, bis der Vorgang abgeschlossen ist, und Sie können eine Protokollmeldung Succeed in adding endpoint: '{endpoint}' sehen, die angibt, dass der Vorgang abgeschlossen ist.

In einigen erwarteten Situationen, z. B. regionsübergreifende Netzwerkprobleme oder Konfigurationsinkonsistenzen auf verschiedenen App-Servern, wird der Stagingzeitraum möglicherweise nicht ordnungsgemäß abgeschlossen. In diesen Fällen wird empfohlen, den App-Server neu zu starten, wenn der Skalierungsprozess nicht ordnungsgemäß funktioniert.

Der standardmäßige Timeoutzeitraum für die Skalierung beträgt fünf Minuten und kann durch Änderung des Werts ServiceOptions.ServiceScaleTimeout angepasst werden. Wenn Sie viele App-Server besitzen, wird empfohlen, den Wert etwas mehr zu erhöhen.

Konfiguration in regionsübergreifenden Szenarien

Das ServiceEndpoint-Objekt verfügt über eine EndpointType-Eigenschaft mit dem Wert primary oder secondary.

Primäre Endpunkte sind bevorzugte Endpunkte, um Clientdatenverkehr zu empfangen, da sie zuverlässigere Netzwerkverbindungen haben. Sekundäre Endpunkte verfügen über weniger zuverlässige Netzwerkverbindungen und werden nur für Server-zu-Client-Datenverkehr verwendet. Sekundäre Endpunkte werden z. B. zum Übertragen von Nachrichten anstelle des Client-zu-Server-Datenverkehrs verwendet.

In regionenübergreifenden Fällen kann das Netzwerk instabil sein. Für einen App-Server in Ost-US befindet primary sich der SignalR-Dienstendpunkt in derselben Region ost-US und Endpunkte in anderen Regionen, die als secondarygekennzeichnet sind. In dieser Konfiguration können Dienstendpunkte in anderen Regionen Nachrichten von diesem Ost-US-App-Server empfangen, aber keine regionsübergreifenden Clients werden an diesen App-Server weitergeleitet. Das folgende Diagramm veranschaulicht diese Architektur:

Cross-Geo Infra

Wenn ein Client mit dem App-Server mit einem Standardrouter versucht/negotiate, wählt das SDK zufällig einen Endpunkt aus der Gruppe der verfügbaren primary Endpunkte aus. Wenn der primäre Endpunkt nicht verfügbar ist, wählt das SDK zufällig aus allen verfügbaren secondary Endpunkten aus. Der Endpunkt wird als verfügbar gekennzeichnet, wenn die Verbindung zwischen Server und Dienstendpunkt aktiv ist.

Wenn ein Client in einem regionsübergreifenden Szenario mit dem in Ost-USA gehosteten App-Server versucht/negotiate, gibt er standardmäßig immer den primary Endpunkt zurück, der sich in derselben Region befindet. Wenn alle OST-US-Endpunkte nicht verfügbar sind, leitet der Router den Client an Endpunkte in anderen Regionen um. Im folgenden Failoverabschnitt wird das Szenario ausführlich beschrieben.

Normal Negotiate

Failover

Wenn kein primary Endpunkt verfügbar ist, wählt der Client /negotiate aus den verfügbaren secondary Endpunkten aus. Dieser Failovermechanismus erfordert, dass jeder Endpunkt als primary Endpunkt für mindestens einen App-Server dient.

Diagram showing the Failover mechanism process.

Nächste Schritte

Sie können mehrere Endpunkte in Szenarien mit hoher Verfügbarkeit und Notfallwiederherstellung verwenden.