複数のインスタンスがある SignalR Service のスケーリング

SignalR Service SDK では、SignalR Service インスタンスの複数のエンドポイントがサポートされています。 この機能を使用して同時接続をスケーリングすることや、リージョンをまたがるメッセージングにこれを使用することができます。

ASP.NET Core の場合

構成から複数のエンドポイントを追加する

SignalR Service 接続文字列のキー Azure:SignalR:ConnectionString または Azure:SignalR:ConnectionString: を使用して構成します。

キーが Azure:SignalR:ConnectionString: で開始する場合、Azure:SignalR:ConnectionString:{Name}:{EndpointType} の形式にする必要があります。ここで、NameEndpointType は、ServiceEndpoint オブジェクトのプロパティであり、コードからアクセス可能です。

次の dotnet コマンドを使用して、複数のインスタンス接続文字列を追加できます。

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>

コードから複数のエンドポイントを追加する

ServicEndpoint クラスには、Azure SignalR Service エンドポイントのプロパティが示されています。 次のように、Azure SignalR Service SDK を使用して複数のインスタンス エンドポイントを構成できます。

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

エンドポイント ルーターをカスタマイズする

既定では、SDK では DefaultEndpointRouter を使用してエンドポイントを選択します。

既定の動作

  1. クライアント要求のルーティング:

    クライアントがアプリ サーバーを使用して /negotiate すると、 既定では、SDK は使用可能な一連のサービス エンドポイントから 1 つのエンドポイントをランダムに選択します。

  2. サーバー メッセージのルーティング:

    特定の接続にメッセージを送信すると、ターゲット接続は現在のサーバーにルーティングされ、メッセージは接続済みエンドポイントに直接送信されます。 それ以外の場合、メッセージはすべての Azure SignalR エンドポイントにブロードキャストされます。

ルーティング アルゴリズムをカスタマイズする

メッセージの送信先エンドポイントを特定する特別な知識を持っている場合は、独自のルーターを作成できます。

次の例では、east- で始まるグループのメッセージを 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);
    }
}

次の例では、既定のネゴシエート動作をオーバーライドし、アプリ サーバーの場所に応じてエンドポイントを選択します。

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

次のように、必ずルーターを DI コンテナーに登録してください。

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

ASP.NET の場合

構成から複数のエンドポイントを追加する

SignalR Service 接続文字列のキー Azure:SignalR:ConnectionString または Azure:SignalR:ConnectionString: を使用して構成します。

キーが Azure:SignalR:ConnectionString: で開始する場合、Azure:SignalR:ConnectionString:{Name}:{EndpointType} の形式にする必要があります。ここで、NameEndpointType は、ServiceEndpoint オブジェクトのプロパティであり、コードからアクセス可能です。

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>

コードから複数のエンドポイントを追加する

ServiceEndpoint クラスには、Azure SignalR Service エンドポイントのプロパティが示されています。 次のように、Azure SignalR Service SDK を使用して複数のインスタンス エンドポイントを構成できます。

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

ルーターをカスタマイズする

ASP.NET SignalR と ASP.NET Core SignalR の唯一の相違は、GetNegotiateEndpoint の http コンテキスト型です。 ASP.NET SignalR の場合は IOwinContext 型です。

次のコードは、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
    }
}

次のように、必ずルーターを DI コンテナーに登録してください。

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

サービス エンドポイントのメトリック

高度なルーターを有効にするために、SignalR サーバー SDK では、サーバーによるスマートな決定に役立つ複数のメトリックが提供されます。 プロパティは ServiceEndpoint.EndpointMetrics の下にあります。

メトリックの名前 説明
ClientConnectionCount サービス エンドポイントのすべてのハブにある同時クライアント接続の合計数
ServerConnectionCount サービス エンドポイントのすべてのハブにある同時サーバー接続の合計数
ConnectionCapacity クライアント接続とサーバー接続を含む、サービス エンドポイントの接続クォータの合計

次のコードは、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
    }
}

動的スケール ServiceEndpoints

SDK バージョン 1.5.0 から、最初に ASP.NET Core バージョンの動的スケール ServiceEndpoints を有効にしています。 そのため、ServiceEndpoint を追加または削除する必要があるときに、アプリ サーバーを再起動する必要はありません。 reloadOnChange: true を指定した appsettings.json のような既定の構成が ASP.NET Core でサポートされているため、コードを変更する必要がなく、本質的にサポートされています。 また、カスタマイズした構成を追加してホット リロードを使用する場合は、「ASP.NET Core の構成」を参照してください。

Note

サーバー/サービスとクライアント/サービスの間の接続設定の時間が異なる場合があることを考慮し、スケール プロセス中にメッセージが失われないようにするため、新しい ServiceEndpoint をクライアントに開く前にサーバー接続の準備が整うのを待つステージング期間があります。 通常、完了するまでに数秒かかり、プロセスが完了したことを示す Succeed in adding endpoint: '{endpoint}' のようなログ メッセージを確認できます。

リージョンをまたがるネットワークの問題や異なるアプリ サーバーでの構成の不一致など、予期される状況によっては、ステージング期間が正しく終了しない可能性があります。 このような場合、スケーリング プロセスが正しく動作していないことがわかったら、アプリ サーバーを再起動することをお勧めします。

スケールの既定のタイムアウト期間は 5 分で、ServiceOptions.ServiceScaleTimeout の値を変更してカスタマイズできます。 多数のアプリ サーバーがある場合は、もう少し値を拡張することをお勧めします。

リージョンをまたがるシナリオでの構成

ServiceEndpoint オブジェクトには、EndpointType プロパティがあり、primary または secondary の値を持ちます。

プライマリ エンドポイントは、より信頼性の高いネットワーク接続を備えているため、クライアント トラフィックを受信するためのエンドポイントとして優先されます。 セカンダリ エンドポイントは、ネットワーク接続の信頼性が低く、サーバーからクライアントへのトラフィックにのみ使用されます。 たとえば、セカンダリ エンドポイントは、クライアントからサーバーへのトラフィックではなく、メッセージのブロードキャストに使用されます。

リージョンをまたがるケースでは、ネットワークが不安定になる場合があります。 米国東部に配置されているアプリ サーバーの場合、同じ米国東部リージョンに配置された SignalR Service エンドポイントが primary であり、他のリージョンにあるエンドポイントは secondary として設定されます。 この構成では、他のリージョンのサービス エンドポイントはこの米国東部のアプリ サーバーからのメッセージを受信できますが、リージョンをまたがるクライアントはこのアプリ サーバーにルーティングされません。 このアーキテクチャを次の図に示します。

Cross-Geo Infra

クライアントが既定のルーターを使用してアプリ サーバーとの /negotiate を試行すると、SDK は使用可能な一連の primary エンドポイントから 1 つのエンドポイントをランダムに選択します。 プライマリ エンドポイントが使用できない場合、SDK は使用可能なすべての secondary エンドポイントからランダムに選択します。 サーバーとサービス エンドポイント間の接続が保持されている場合、エンドポイントは使用可能としてマークされます。

リージョンをまたがるシナリオでは、米国東部でホストされているアプリ サーバーとの /negotiate をクライアントが試行すると、既定では、同じリージョンに配置されている primary エンドポイントが常に返されます。 米国東部のすべてのエンドポイントが使用不可の場合、ルーターはクライアントを他のリージョンのエンドポイントにリダイレクします。 次の「フェールオーバー」セクションでこのシナリオについて詳しく説明します。

Normal Negotiate

[フェールオーバー]

使用可能な primary エンドポイントがない場合、クライアントの /negotiate により使用可能な secondary エンドポイントが選択されます。 このフェールオーバー メカニズムでは、各エンドポイントが少なくとも 1 つのアプリ サーバーに対する primary エンドポイントとして機能する必要があります。

Diagram showing the Failover mechanism process.

次のステップ

高可用性およびディザスター リカバリーのシナリオで複数のエンドポイントを使用できます。