带有Redis背板的SignalR在F5后面 - StatusCode:400,ReasonPhrase:' Bad Request'

时间:2014-11-13 23:16:51

标签: c# asp.net signalr f5

我在Server 2012 R2,启用了WebSockets的IIS 8.5上使用SignalR版本2.1.2和SignalR.Redis 2.1.2。

一切都在我的开发环境中完美运行。我甚至可以在配置为使用相同背板的站点的不同服务器(例如,http machine1 / myapp / signalr,http machine2 / myapp / signalr)上站起来,并且两个UI都能完美地向他们发送消息。

然后我将“myapp”移动到我们的下一个环境,这是一个由2台机器组成的集群,它们位于F5负载均衡器后面,使用dns别名设置路由到F5,然后循环“myapp”。网站本身可以很好地连接到信号器,并且可以接收它订阅的已发布消息,但是当我尝试通过别名(例如http myappalias / signalr)发布到网站时,我得到400,错误请求错误响应。以下是错误的示例。

  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()上失败”.Waite()“

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

    }

}

我下载客户端源代码,并直接连接而不是nuget包,所以我可以逐步完成所有操作。我似乎成功协商,然后尝试与SSE和LongPolling传输“连接”,但两者都失败了。

问题1.1

任何人都知道Signalr for .NET的替代方案,支持使用负载均衡进行扩展,而不是“我想把我的头发拉出来”吗?

2 个答案:

答案 0 :(得分:1)

通过切换" MyApp"的配置文件来解决问题。在F5中,使用" source_addr"作为父配置文件内置到F5中的配置文件,超时为1小时。以下是该配置文件的功能描述:

  

源地址亲和性持久性也称为简单持久性,   源地址相关性持久性支持TCP和UDP协议,   并基于。将会话请求定向到同一服务器   数据包的源IP地址。

修改

最终"工作"有一段时间,但如果我部署一个发布者(通过信号器客户端简单发布的东西)而不重新发布Hub,则发布者会一次又一次地尝试连接。 UHG。

答案 1 :(得分:1)

没有必要配置源地址亲缘性以在负载均衡器后面使用SignalR。设置会话亲和力肯定没错,但这并不能解决你的根本问题。

如果仔细查看400响应的内容,您可能会看到类似于&#34的消息; ConnectionId的格式不正确。"

SignalR使用服务器的计算机密钥创建反CSRF令牌,但这要求服务器场中的所有服务器共享一个计算机密钥,以便在SignalR请求跳转服务器时正确解密令牌。您看到成功的/ negotiate请求是检索反CSRF令牌的请求。当SignalR客户端然后使用反CSRF令牌发出/ connect请求时,它失败了,因为/ connect请求是由不创建令牌并且无法解密它的其他服务器处理的。

这解释了为什么设置会话亲和性可以解决您的问题,但共享机器密钥将帮助您避免此问题,即使会话亲和力出现问题。

以下是遇到类似问题的人在GitHub上提出的问题:https://github.com/SignalR/SignalR/issues/2292