複数のインスタンスがある 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}
の形式にする必要があります。ここで、Name
と EndpointType
は、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 を使用してエンドポイントを選択します。
既定の動作
クライアント要求のルーティング:
クライアントがアプリ サーバーを使用して
/negotiate
すると、 既定では、SDK は使用可能な一連のサービス エンドポイントから 1 つのエンドポイントをランダムに選択します。サーバー メッセージのルーティング:
特定の接続にメッセージを送信すると、ターゲット接続は現在のサーバーにルーティングされ、メッセージは接続済みエンドポイントに直接送信されます。 それ以外の場合、メッセージはすべての 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}
の形式にする必要があります。ここで、Name
と EndpointType
は、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
として設定されます。 この構成では、他のリージョンのサービス エンドポイントはこの米国東部のアプリ サーバーからのメッセージを受信できますが、リージョンをまたがるクライアントはこのアプリ サーバーにルーティングされません。 このアーキテクチャを次の図に示します。
クライアントが既定のルーターを使用してアプリ サーバーとの /negotiate
を試行すると、SDK は使用可能な一連の primary
エンドポイントから 1 つのエンドポイントをランダムに選択します。 プライマリ エンドポイントが使用できない場合、SDK は使用可能なすべての secondary
エンドポイントからランダムに選択します。 サーバーとサービス エンドポイント間の接続が保持されている場合、エンドポイントは使用可能としてマークされます。
リージョンをまたがるシナリオでは、米国東部でホストされているアプリ サーバーとの /negotiate
をクライアントが試行すると、既定では、同じリージョンに配置されている primary
エンドポイントが常に返されます。 米国東部のすべてのエンドポイントが使用不可の場合、ルーターはクライアントを他のリージョンのエンドポイントにリダイレクします。 次の「フェールオーバー」セクションでこのシナリオについて詳しく説明します。
[フェールオーバー]
使用可能な primary
エンドポイントがない場合、クライアントの /negotiate
により使用可能な secondary
エンドポイントが選択されます。 このフェールオーバー メカニズムでは、各エンドポイントが少なくとも 1 つのアプリ サーバーに対する primary
エンドポイントとして機能する必要があります。
次のステップ
高可用性およびディザスター リカバリーのシナリオで複数のエンドポイントを使用できます。