最終更新日 2025年06月16日(月)
Heroku にASP.NET Core SignalR アプリをデプロイするには、最小限の設定で済みます。ただし、SignalR アプリを複数の dyno にスケーリングするには、次の設定が必要です。
- 接続ルーティング: Heroku のステートレスルーティング環境でクライアント接続を安定させます。
- メッセージ調整: dyno 間でメッセージを共有し、接続されているすべてのクライアントに届くようにします。
接続ルーティング
デフォルトでは、SignalR クライアントは複数の HTTP 要求を伴うトランスポートネゴシエーションプロセスを通じて接続します。Heroku のルーターはデフォルトでステートレスであるため、各リクエストは利用可能な任意の Web dyno に送信されます。リクエストが以前のリクエストで保持していたクライアントのセッション状態を持たない dyno に到達すると、クライアント接続が切断される可能性があります。
この問題は、WebSocket (推奨) かセッションアフィニティを使用することで解決できます。
WebSocket による接続ルーティングの管理
クライアントとサーバーの双方にとって最も信頼性の高いソリューションであるため、WebSocket を使用することを推奨します。WebSocket はトランスポートネゴシエーションを排除し、Heroku のステートレスルーター環境でも安定して動作します。詳細は、「Heroku の WebSocket」を参照してください。
WebSocket を使用すると、SignalR の自動トランスポートフォールバック機能を利用できなくなります。ただし、最新のブラウザやクライアントはどれも WebSocket に対応しているため、最も信頼性の高い方法として WebSocket をお勧めします。
クライアント設定: クライアントコードでトランスポートネゴシエーションをスキップします
const connection = new signalR.HubConnectionBuilder()
.withUrl("/noteHub", {
skipNegotiation: true,
transport: signalR.HttpTransportType.WebSockets
})
.build();
サーバー設定: WebSocket のみへの接続を強制します
app.MapHub<YourApp.NoteHub>("/noteHub", options =>
{
options.Transports =
Microsoft.AspNetCore.Http.Connections.HttpTransportType.WebSockets;
});
セッションアフィニティによる接続ルーティングの管理
WebSocket がない場合、SignalR はロングポーリングなどの HTTP ベースのトランスポートを使用します。ロングポーリングでは、リアルタイム接続を維持するために複数のリクエストが必要です。
WebSocket を使用できないクライアントをサポートする必要がある場合は、Heroku のセッションアフィニティ機能を使用できます。セッションアフィニティは、Common Runtime のアプリでのみ使用できます。詳細は、「セッションアフィニティ」を参照してください。
セッションアフィニティを有効にするには、次のコマンドを実行します。
$ heroku features:enable http-session-affinity
メッセージ調整
マルチ dyno 環境では、サーバー間でメッセージを共有するためのバックプレーンも必要です。バックプレーンを設定すると、どの dyno から送られたメッセージも、対象となるすべてのクライアントに確実に配信されるようになります。
バックプレーンを使用したスケーリングについての詳細は、Microsoft の SignalR スケーリングのドキュメントを参照してください。
Redis バックプレーンの設定
Heroku Key-Value Store アドオンを使用して Redis バックプレーンを設定できます。
Heroku Key-Value Store アドオンを追加します。
$ heroku addons:create heroku-redis:miniSignalR Redis パッケージをインストールします。
$ dotnet add package Microsoft.AspNetCore.SignalR.StackExchangeRedisSignalR のバックプレーンを構成します。
// Program.cs // ... using StackExchange.Redis; var builder = WebApplication.CreateBuilder(args); var redisUrl = Environment.GetEnvironmentVariable("REDIS_URL"); if (!string.IsNullOrEmpty(redisUrl)) { // Parse the Redis URL for our client var uri = new Uri(redisUrl); var userInfoParts = uri.UserInfo.Split(':'); if (userInfoParts.Length != 2) { throw new InvalidOperationException("REDIS_URL is not in the expected format ('redis://user:password@host:port')"); } var configurationOptions = new ConfigurationOptions { EndPoints = { { uri.Host, uri.Port } }, Password = userInfoParts[1], Ssl = true, }; // Handle Heroku's self-signed certificates // See: https://devcenter.heroku.com/articles/heroku-redis#security-and-compliance configurationOptions.CertificateValidation += (sender, cert, chain, errors) => true; // Set up the SignalR backplane builder.Services.AddSignalR().AddStackExchangeRedis(options => { options.Configuration = configurationOptions; options.Configuration.ChannelPrefix = "YourAppName"; }); } else { // Local development fallback builder.Services.AddSignalR(); } var app = builder.Build(); // ... app.Run();
.NET アプリの接続方法についての詳細は、Heroku Key-Value Store のドキュメントを参照してください。
まとめ
Heroku で SignalR アプリを適切にスケーリングするには
単一の dyno の場合、特別な設定は必要ありません。
複数の dyno の場合は以下が必要です。
- クライアントとサーバーの両方で WebSocket のみを使用するよう設定するか (推奨)、セッションアフィニティを使用します。
- Heroku Key-Value Store を使用して設定した Redis バックプレーン。