4

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

一切都在我的开发环境中完美运行。我什至可以在配置为使用相同背板的站点的不同服务器(例如 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().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 的替代方案,它支持通过负载平衡进行扩展,而不是“我想拔掉头发”的那种方式?

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 回答