次の方法で共有


Azure SignalR Service の回復性とディザスター リカバリー

回復性とディザスター リカバリーは、各種オンライン システムに共通の要件です。 Azure SignalR Service は既に 99.9% の可用性を提供していますが、それはあくまでリージョンごとのサービスです。 リージョン全体が停止しても、サービス インスタンスは常に 1 つのリージョンで実行されているため、他のリージョンにフェールオーバーされることはありません。

リージョンのディザスター リカバリーを行う場合は、次の 2 つの方法をお勧めします。

  • geo レプリケーションを有効にする (簡単な方法)。 この機能は、リージョンのフェールオーバーを自動的に処理します。 有効にすると、Azure SignalR インスタンスは 1 つだけのままで、コードの変更は発生しません。 詳細については、geo レプリケーションを確認してください。
  • サービス SDK で複数のエンドポイントを使用する。 Microsoft のサービス SDK は複数の SignalR サービス インスタンスをサポートしており、一部のインスタンスが使用できない場合は自動的に他のインスタンスに切り替わります。 この機能を利用すれば、障害発生時に復旧できるようになりますが、適切なシステム トポロジをご自分で設定する必要があります。 このドキュメントでは、その方法について説明します。

SignalR サービスの高可用性アーキテクチャ

リージョンをまたぐ回復性を SignalR サービスに確保するには、複数のサービス インスタンスを異なるリージョンにセットアップする必要があります。 そうすることで、1 つのリージョンがダウンしても、その他のリージョンをバックアップとして使用することができます。 アプリ サーバーが複数のサービス インスタンスに接続する際には、プライマリとセカンダリという 2 つのロールが存在します。 プライマリはオンライン トラフィックの受信を担当するインスタンスであり、セカンダリは完全に機能するフォールバック インスタンスとして機能します。 この SDK の実装では、ネゴシエートからはプライマリ エンドポイントだけが返されるため、正常時にクライアントが接続するのはプライマリ エンドポイントだけです。 しかし、プライマリ インスタンスがダウンしているときは、クライアントが引き続き接続できるよう、ネゴシエートでセカンダリ エンドポイントが返されます。 プライマリ インスタンスとアプリ サーバーは、通常のサーバー接続を介して接続されますが、セカンダリ インスタンスとアプリ サーバーは、弱い接続と呼ばれる特殊な接続を介して接続されます。 弱い接続の特徴の 1 つは、セカンダリ インスタンスが別のリージョンにあるため、クライアント接続ルーティングを受け入れることができないことです。 クライアントを別のリージョンにルーティングすることは最適な選択ではありません (待ち時間が長くなります)。

1 つのサービス インスタンスを複数のアプリ サーバーに接続するとき、そのインスタンスには、複数のロールを割り当てることができます。 リージョンをまたぐシナリオでは、SignalR サービス インスタンスとアプリ サーバーとを 2 ペア以上用意するのが一般的な構成です。 それぞれのペア内でアプリ サーバーと SignalR サービスとが同じリージョン内に存在し、SignalR サービスは、プライマリ ロールとしてアプリ サーバーに接続されます。 ペアとペアの間でも、アプリ サーバーと SignalR サービスとが接続されますが、SignalR は、別のリージョン内のサーバーに接続するときにはセカンダリとなります。

このトポロジであっても、すべてのアプリ サーバーと SignalR サービス インスタンスが相互接続されているので、一方のサーバーからすべてのクライアントにメッセージを配信することはできます。 しかしクライアントが接続されているときは、最適なネットワーク待ち時間を確保するために、そのクライアントは、同じリージョン内のアプリ サーバーにルーティングされます。

次のダイアグラムは、このようなトポロジを示しています。

この図は、それぞれがアプリ サーバーと SignalR サービスを持つ 2 つのリージョンを示しています。各サーバーは、プライマリとしてそのリージョンの SignalR サービスに関連付けられ、セカンダリとして他のリージョンのサービスに関連付けられています。

複数の SignalR サービス インスタンスを構成する

複数の SignalR サービス インスタンスは、アプリ サーバーと Azure Functions の両方でサポートされています。

SignalR サービスとアプリ サーバーまたは Azure Functions を各リージョンに作成したら、すべての SignalR サービス インスタンスに接続するようにアプリ サーバーまたは Azure Functions を構成できます。

config による方法

環境変数/アプリの設定/web.cofig から Azure:SignalR:ConnectionString という名前の config エントリで SignalR サービスの接続文字列を設定する方法をご存知のはずです。 複数のエンドポイントがある場合は、複数の config エントリでそれらを設定してください。それぞれ次の形式で設定します。

Azure:SignalR:ConnectionString:<name>:<role>

ConnectionString の <name> はエンドポイントの名前であり、<role> はそのロール (プライマリまたはセカンダリ) です。 名前は省略可能ですが、複数のエンドポイント間でルーティングの動作をさらにカスタマイズしたい場合に役立ちます。

コードによる方法

どこか他の場所に接続文字列を保存したい場合は、コードからそれらを読み取って、AddAzureSignalR() (ASP.NET Core の場合) または MapAzureSignalR() (ASP.NET の場合) を呼び出す際にパラメーターとして使用することもできます。

コード例を次に示します。

ASP.NET Core:

services.AddSignalR()
        .AddAzureSignalR(options => options.Endpoints = new ServiceEndpoint[]
        {
            new ServiceEndpoint("<connection_string1>", EndpointType.Primary, "region1"),
            new ServiceEndpoint("<connection_string2>", EndpointType.Secondary, "region2"),
        });

ASP.NET:

app.MapAzureSignalR(GetType().FullName, hub,  options => options.Endpoints = new ServiceEndpoint[]
    {
        new ServiceEndpoint("<connection_string1>", EndpointType.Primary, "region1"),
        new ServiceEndpoint("<connection_string2>", EndpointType.Secondary, "region2"),
    };

複数のプライマリまたはセカンダリ インスタンスを構成できます。 プライマリおよび/またはセカンダリのインスタンスが複数ある場合、ネゴシエートは次の順序でエンドポイントを返します。

  1. 少なくとも 1 つのプライマリ インスタンスがオンラインになっている場合は、ランダムなオンラインのプライマリ インスタンスを返します。
  2. すべてのプライマリ インスタンスがダウンしている場合は、ランダムなオンラインのセカンダリ インスタンスを返します。

Azure Functions SignalR のバインドの場合

複数の SignalR Service インスタンスを有効にするには、次のことを行う必要があります。

  1. Persistent の転送の種類を使用します。

    既定のトランスポートの種類は Transient モードです。 次のエントリを local.settings.json ファイルまたは Azure のアプリケーション設定に追加する必要があります。

    {
        "AzureSignalRServiceTransportType":"Persistent"
    }
    

    Note

    Transient モードから Persistent モードに切り替えると、JSON シリアル化の動作が変わる可能性があります。これは、Transient モードでは、ハブ メソッドの引数のシリアル化に Newtonsoft.Json ライブラリが使用されますが、Persistent モードでは、System.Text.Json ライブラリが既定で使用されるためです。 System.Text.Json には、Newtonsoft.Json との既定の動作にいくつかの重要な違いがあります。 Persistent モードで Newtonsoft.Json を使用する場合は、構成項目 (local.settings.json ファイルでは "Azure:SignalR:HubProtocol":"NewtonsoftJson"、または Azure portal では Azure__SignalR__HubProtocol=NewtonsoftJson) を追加できます。

  2. 構成で複数の SignalR Service エンドポイント エントリを構成します。

    ServiceEndpoint オブジェクトを使用して SignalR Service インスタンスを表します。 エントリ キーに <EndpointName><EndpointType>、エントリ値に接続文字列を使用してサービス エンドポイントを定義できます。 キーの形式は次のとおりです。

    Azure:SignalR:Endpoints:<EndpointName>:<EndpointType>
    

    <EndpointType> は省略可能であり、既定値は primary です。 以下のサンプルを参照してください。

    {
        "Azure:SignalR:Endpoints:EastUs":"<ConnectionString>",
    
        "Azure:SignalR:Endpoints:EastUs2:Secondary":"<ConnectionString>",
    
        "Azure:SignalR:Endpoints:WestUs:Primary":"<ConnectionString>"
    }
    

    Note

    • Azure portal の App Service で Azure SignalR エンドポイントを構成するときは、必ずキー内の ":""__" (二重アンダースコア) に置き換えます。 理由については、環境変数に関する記事を参照してください。

    • また、キー {ConnectionStringSetting} を使用して構成された接続文字列 (既定値は "AzureSignalRConnectionString") は、名前が空のプライマリ サービス エンドポイントとして認識されます。 ただし、この構成スタイルは複数のエンドポイントには推奨されません。

Management SDK の場合

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

SignalR Service 接続文字列のキー Azure:SignalR:Endpoints を使用して構成します。 キーの形式は Azure:SignalR:Endpoints:{Name}:{EndpointType} にする必要があります。この NameEndpointTypeServiceEndpoint オブジェクトのプロパティであり、コードからアクセスできます。

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

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

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

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

var serviceManager = new ServiceManagerBuilder()
                    .WithOptions(option =>
                    {
                        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"),
                        };
                    })
                    .BuildServiceManager();

フェールオーバーのシーケンスとベスト プラクティス

以上で、適切なシステム トポロジのセットアップが完了しました。 一方の SignalR Service インスタンスがダウンすると、オンライン トラフィックが別のインスタンスにルーティングされます。 プライマリ インスタンスがダウン (およびその後しばらくしてから復旧) したときに発生する状況を以下に示します。

  1. プライマリ サービス インスタンスがダウンすると、このインスタンスにおけるすべてのサーバー接続が切断されます。
  2. このインスタンスは、そこに接続されているすべてのサーバーによってオフラインとしてマークされます。また、ネゴシエートでこのエンドポイントは返されなくなり、セカンダリのエンドポイントが返されるようになります。
  3. また、このインスタンスに対するクライアント接続もすべて閉じられ、クライアントの再接続が行われます。 以後アプリ サーバーからはセカンダリ エンドポイントが返されるので、クライアントはセカンダリ インスタンスに接続されます。
  4. これですべてのオンライン トラフィックがセカンダリ インスタンスに向かうようになりました。 セカンダリはすべてのアプリ サーバーに接続されているため、サーバーからクライアントへのメッセージは依然としてすべて配信されます。 しかし、クライアントからサーバーへのメッセージのルーティング先は、同じリージョン内のアプリ サーバーに限られます。
  5. プライマリ インスタンスが復旧してオンラインに戻った後、アプリ サーバーはプライマリ インスタンスへの接続を再度確立し、それをオンラインとしてマークします。 ネゴシエートでは再びプライマリ エンドポイントが返されるようになるので、新しいクライアントは元どおりプライマリに接続されます。 ただし、既存のクライアントは切断されず、自ら切断するまでそのままセカンダリにルーティングされます。

以下の図は、SignalR サービスにおけるフェールオーバーの動作を示したものです。

図.1 フェールオーバー前 フェールオーバー前

図.2 フェールオーバー後 フェールオーバー後

図.3 プライマリの復旧後間もなく プライマリの復旧後間もなく

正常時には、オンライン トラフィック (青色) がプライマリのアプリ サーバーと SignalR サービスにのみ向かうことがわかります。 フェールオーバー後は、セカンダリのアプリ サーバーと SignalR サービスもアクティブになります。 プライマリの SignalR サービスがオンラインに戻った後、新しいクライアントはプライマリの SignalR に接続されます。 一方、既存のクライアントはそのままセカンダリに接続された状態になるので、両方のインスタンスにトラフィックが向かうことになります。 既存のクライアントがすべて切断されると、システムが正常な状態に戻ります (図 1)。

リージョンをまたぐ高可用性アーキテクチャを導入する場合、主に次の 2 つのパターンがあります。

  1. 1 つ目は、アプリ サーバーと SignalR サービス インスタンスから成る一方のペアですべてのオンライン トラフィックを処理し、もう一方のペアはバックアップとして使用する方法です (これは "アクティブ/パッシブ" と呼ばれます。図1 を参照)。
  2. もう 1 つは、アプリ サーバーと SignalR サービス インスタンスから成るペアを 2 つ (またはそれ以上) 用意し、それぞれのペアがオンライン トラフィックを分担して処理し、他のペアのバックアップとして機能する方法です (これは "アクティブ/アクティブ" と呼ばれます。図 3 と同様)。

SignalR サービスは両方のパターンに対応できます。主な違いはアプリ サーバーの導入方法です。 アプリ サーバーがアクティブ/パッシブである場合、SignalR Service もアクティブ/パッシブになります (プライマリのアプリ サーバーから返されるのはそのプライマリ SignalR Service インスタンスのみであるため)。 アプリ サーバーがアクティブ/アクティブである場合、SignalR Service もアクティブ/アクティブになります (すべてのアプリ サーバーから、それぞれのプライマリ SignalR インスタンスが返されるので、そのすべてでトラフィックを受けることができます)。

どちらのパターンを使用するにしても、アプリ サーバーには、それぞれの SignalR Service インスタンスをプライマリとして接続する必要がある点に注意してください。

また、SignalR 接続 (長時間接続) の性質上、障害とフェールオーバーが発生すると、クライアントでは接続の切断が生じます。 そのようなケースはクライアント側で処理して、エンド カスタマーからは見えないようにする必要があります。 たとえば、接続が閉じられた後で再接続を行うことが考えられます。

フェールオーバーをテストする方法

フェールオーバーをトリガーするには、次の手順に従います。

  1. ポータルのプライマリ リソースの [ネットワーク] タブで、パブリック ネットワーク アクセスを無効にします。 リソースでプライベート ネットワークが有効になっている場合は、"アクセス制御ルール" を使用して、すべてのトラフィックを拒否します。
  2. プライマリ リソースを再起動します

次のステップ

この記事では、SignalR サービスに回復性を持たせるためのアプリケーションの構成方法について説明しました。 SignalR サービスにおける接続のルーティングとサーバー/クライアント接続の詳細については、SignalR サービスの内部について説明したこちらの記事を参照してください。

大量の接続を処理するために複数のインスタンスを同時に使用するスケーリング シナリオ (シャーディングなど) については、複数のインスタンスをスケーリングする方法に関するページを参照してください。

複数の SignalR サービス インスタンスで Azure Functions を構成する方法の詳細については、 Azure Functions での複数の Azure SignalR サービス インスタンスのサポートに関する記事を参照してください。