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

我在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,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); } } } } 

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

问题1.1

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

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

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

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

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

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

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

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

编辑

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