我在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的替代方案,支持使用负载均衡进行扩展,而不是“我想把我的头发拉出来”吗?
答案 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。