4

サーバー 2012 R2、IIS 8.5 で WebSockets が有効になっている SignalR.Redis 2.1.2 で SignalR バージョン 2.1.2 を使用しています。

すべてが私の開発環境で完全に実行されています。同じバックプレーンを使用するように構成されたサイトの異なるサーバー (http machine1/myapp/signalr、http machine2/myapp/signalr など) にコピーを立ち上げることもでき、両方の UI がそれらに完全に発行されたメッセージを取得します。

次に、「myapp」を次の環境に移動しました。これは、F5 ロード バランサーの背後にある 2 台のマシンのクラスターであり、F5 にルーティングするように dns エイリアスを設定し、ラウンド ロビン「myapp」を使用します。Web サイト自体は signalr に問題なく接続でき、サブスクライブしている発行済みメッセージを受信できますが、エイリアス (http myappalias/signalr など) を介してサイトに発行しようとすると、400、Bad Request エラー応答が返されます。エラーの例を次に示します。

  InnerException: Microsoft.AspNet.SignalR.Client.Infrastructure.StartException
       _HResult=-2146233088
       _message=Error during start request. Stopping the connection.
       HResult=-2146233088
       IsTransient=false
       Message=Error during start request. Stopping the connection.
       InnerException: System.AggregateException
            _HResult=-2146233088
            _message=One or more errors occurred.
            HResult=-2146233088
            IsTransient=false
            Message=One or more errors occurred.
            InnerException: Microsoft.AspNet.SignalR.Client.HttpClientException
                 _HResult=-2146233088
                 _message=StatusCode: 400, ReasonPhrase: 'Bad Request', Version: 1.1, Content: System.Net.Http.StreamContent, Headers:
{
  Pragma: no-cache
  Transfer-Encoding: chunked
  X-Content-Type-Options: nosniff
  Persistent-Auth: true
  Cache-Control: no-cache
  Date: Thu, 13 Nov 2014 22:30:22 GMT
  Server: Microsoft-IIS/8.5
  X-AspNet-Version: 4.0.30319
  X-Powered-By: ASP.NET
  Content-Type: text/html
  Expires: -1
}

「connection.Start().Wait()」で失敗する各環境にテスト メッセージを発行するために使用しているテスト コードを次に示します。

class Program
{
    static void Main(string[] args)
    {
        var connection = new HubConnection("http://myappalias/signalr");

        connection.Credentials = System.Net.CredentialCache.DefaultNetworkCredentials;

        var proxy = connection.CreateHubProxy("MyAppHub");

        connection.Start().Wait();

        ConsoleKeyInfo key = Console.ReadKey();

        do
        {


            proxy.Invoke("NewMessage", new Message() { Payload = "Hello" });

            Console.WriteLine("Message fired.");

            key = Console.ReadKey();

        } while (key.Key != ConsoleKey.Escape);
    }
}

ここで、「myappalias」を使用せずにサーバーに直接アクセスすると、完全に機能します。F5 に問題があるか、このシナリオではクライアントを別の方法で構成する必要があるか、signlar のスタートアップ クラスをセットアップするときに別のことを行う必要があるようです。これは、私が使用しているスタートアップ クラスの例です。

[assembly: OwinStartup(typeof(MyApp.Startup))]
namespace MyApp
{
    public class Startup
    {
        private static readonly ILog log = LogManager.GetLogger
        (System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);

        public void Configuration(IAppBuilder app)
        {
            try
            {
                log.Debug(LoggingConstants.Begin);

                string redisServer = ConfigurationManager.AppSettings["redis:server"];

                int redisPort = Convert.ToInt32(ConfigurationManager.AppSettings["redis:port"]);

                HubConfiguration configuration = new HubConfiguration();
                configuration.EnableDetailedErrors = true;
                configuration.EnableJavaScriptProxies = false;
                configuration.Resolver = GlobalHost.DependencyResolver.UseRedis(redisServer, redisPort, string.Empty, "MyApp");

                app.MapSignalR("/signalr", configuration);   

                log.Info("SIGNALR - Startup Complete");
            }
            finally
            {
                log.Debug(LoggingConstants.End);
            }
        }

    }

}

クライアントのソース コードをダウンロードし、ナゲット パッケージの代わりにそれを直接配線して、すべてをステップ実行できるようにしました。ネゴシエーションに成功し、SSE と LongPolling トランスポートに「接続」しようとしているようですが、両方で失敗しています。

質問 1.1

Signalr for .NET の代替手段を知っている人はいますか。これは、負荷分散によるスケーリングをサポートし、「髪の毛を引き抜きたい」というような方法ではありませんか?

4

2 に答える 2

1

ロード バランサーの背後で SignalR を使用するためにソース アドレス アフィニティを構成する必要はありません。セッション アフィニティを設定することは間違いではありませんが、根本的な問題は解決しません。

400 応答の内容をよく見ると、「ConnectionId の形式が正しくありません」のようなメッセージが表示される可能性があります。

SignalR はサーバーのマシン キーを使用して CSRF 対策トークンを作成しますが、これには、SignalR がホップ サーバーを要求するときにトークンが適切に復号化されるように、ファーム内のすべてのサーバーがマシン キーを共有する必要があります。成功した /negotiate リクエストは、アンチ CSRF トークンを取得するリクエストです。その後、SignalR クライアントがアンチ CSRF トークンを使用して /connect 要求を作成すると、/connect 要求がトークンを作成していない別のサーバーによって処理され、暗号化を解除できないため、失敗しました。

これは、セッション アフィニティを設定することで問題が解決した理由を説明していますが、マシン キーを共有すると、セッション アフィニティに問題が発生した場合でも、この問題を回避するのに役立ちます。

同様の問題を経験した人が GitHub に提出した問題を次に示します: https://github.com/SignalR/SignalR/issues/2292

于 2014-11-14T19:44:17.473 に答える
1

この問題は、F5 の「MyApp」のプロファイルを、F5 に組み込まれている「source_addr」プロファイルを親プロファイルとして使用するように切り替えることで解決されました。タイムアウトは 1 時間です。そのプロファイルが何をするかの説明は次のとおりです。

送信元アドレス アフィニティ パーシステンス シンプル パーシスタンスとも呼ばれる送信元アドレス アフィニティ パーシスタンスは、TCP および UDP プロトコルをサポートし、パケットの送信元 IP アドレスのみに基づいてセッション要求を同じサーバーに転送します。

編集

これはしばらくの間「動作」していましたが、ハブを再発行せずにパブリッシャー (単にシグナル クライアントを介して発行するもの) をデプロイすると、パブリッシャーは何度も何度も接続しようとしてタイムアウトします。うーん。

于 2014-11-14T17:36:36.210 に答える