SignalR 集线器挂在 IIS 上
SignalR hub hangs on IIS
我们在 ASP.NET 网络应用程序中使用 SignalR 库。代码如下所示:
服务器:
[HubName("ticketsCounterHub")]
public class MassivePrintHub : Hub
{
public void PostTicketsCount(long count)
{
Clients.All.Send(count);
}
}
public class HubFactory
{
private HubFactory() {}
public static readonly HubFactory Current = new HubFactory();
public IHubProxy GetMassivePrintHubProxy()
{
var hubConnection = new HubConnection(ConfigUtils.GetRequiredSettingValue("adminPath"));
var hubProxy = hubConnection.CreateHubProxy("ticketsCounterHub");
hubConnection.Start().Wait();
return hubProxy;
}
}
客户(JavaScript):
MassivePrintApp.controller("ListController", function ($scope, Dates) {
var hubManager = (function () {
var massivePrintHub = $.connection.ticketsCounterHub;
$.connection.hub.start();
return { massivePrintHub: massivePrintHub };
} ());
hubManager.massivePrintHub.client.Send = function (ticketsCount) {
$scope.action.Quantity = ticketsCount;
$scope.$digest();
};
});
代码的关键部分在 MVC 控制器中:
public FileResult PrintAction(int actionId, int count, DateTime actionDate, bool isThermo=false)
{
var ticketsCount = _ticketService.GetTicketsInStatusCount(actionId, actionDate, TicketStatusEnum.ToPrint);
HubFactory.Current.GetMassivePrintHubProxy().Invoke("PostTicketsCount", ticketsCount);
var stream = new MemoryStream();
xmlResponse.Save(stream);
stream.Flush();
stream.Position = 0;
return File(stream,ContentTypeEnum.XML.ToString(),String.Format("массовая {0} мероприятия {1} {2}шт.xml", isThermo?"термопечать":"печать", action.Artist,count));
}
如您所见,我们有这一行:
HubFactory.Current.GetMassivePrintHubProxy().Invoke("PostTicketsCount", ticketsCount);
这导致了问题,即每当我们调用它时,都会在 IIS 的 "Requests" 部分添加一个集线器实例。
我知道我们已经在 JavaScript 代码中启动了集线器,但我不确定如何使用现有连接或如何摆脱 HubFactory 或删除创建的集线器实例。
而且我不明白为什么集线器挂在 IIS 上。
我想从一个更简单的例子开始会对你有很大帮助。之后,您可以考虑以不同方式托管您的 SignalR 服务器(控制台应用程序或 Windows 服务),基础知识不会改变
(首次安装SignalR: NuGet: install-package Microsoft.AspNet.SignalR
)
我制作了一个简单的网络应用示例。该项目有一个 Hub class:
using Microsoft.AspNet.SignalR;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
namespace SRTest
{
public class MassivePrintHub : Hub
{
private static IHubContext hubContext = GlobalHost.ConnectionManager.GetHubContext<MassivePrintHub>();
// Can be called from your Javascript code
public void PostTicketsCount(long count)
{
Clients.All.Send(count);
}
// Can be called from your c# code
public static void Static_PostTicketsCount(long count)
{
hubContext.Clients.All.Send(count);
}
}
}
Owin 初创公司 class:
using Microsoft.AspNet.SignalR;
using Microsoft.Owin;
using Owin;
[assembly: OwinStartup(typeof(SRTest.Startup))]
namespace SRTest
{
public class Startup
{
public void Configuration(IAppBuilder app)
{
var hubConfiguration = new HubConfiguration();
hubConfiguration.EnableDetailedErrors = true;
app.MapSignalR(hubConfiguration);
}
}
}
页面(Razor 只是为了能够调用从后端调用 c# class 到 post 消息的模拟器):
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>TEST PAGE</title>
<!--Reference the jQuery library. -->
<script src='Scripts/jquery-1.6.4.js'></script>
<!--Reference the SignalR library. -->
<script src='Scripts/jquery.signalR-2.2.0.js'></script>
<!--Reference the autogenerated SignalR hub script. -->
<script src="signalr/hubs"></script>
</head>
<body>
THIS IS A TEST PAGE
<!-- Call simulator (trigger event every 5 seconds) -->
@{SRTest.SendFromBackEnd.SimulateSend();}
<script>
$(function () {
var printHub = $.connection.massivePrintHub;
// when send event happens
printHub.client.send = function (count) {
console.log("Send " + count + " tickets");
};
$.connection.hub.start().done(function () {
console.log("Connected");
});
$.connection.hub.logging = true;
});
</script>
</body>
</html>
并且我添加了一个虚拟 class,它每 5 秒通过 hubcontext 触发一次事件。
using System.Threading;
using System.Web;
namespace SRTest
{
public class SendFromBackEnd
{
public static void SimulateSend()
{
new Thread(() =>
{
Thread.CurrentThread.IsBackground = true;
while (true)
{
MassivePrintHub.Static_PostTicketsCount(2);
Thread.Sleep(5000);
}
}).Start();
}
}
}
我在 SignalR 中添加了一些日志记录,添加了一些调试点,这将帮助您了解基础知识,然后构建您计划构建的内容会容易得多。
编辑
关于挂起的请求:只要您有一个客户端通过 SSE 或 AJAX 长轮询连接到您的 SignalR 服务器,您就会有一个正在进行的请求,该请求永远不会完成。 (在 AJAX 长轮询的情况下,它会在很短的时间内完成并返回)。在我只使用 Javascript 客户端的应用程序中,如果我正在监听事件的页面打开,我只会看到请求。如果没有页面或静态页面打开则没有请求。
在我使用 .NET 客户端的应用程序中,只要这两个应用程序是 运行,并且都执行了 Sartup classes,请求将始终存在,即使没有打开页面。 (因为 .NET 客户端仍在侦听事件。)
更多信息:http://hanselminutes.com/291/damian-edwards-explains-the-realtime-web-for-aspnet-with-signalr
这是一个与线程相关的问题。像这样尝试
Task.Run(() => connection.Start().ContinueWith(task =>
{
.....
})).Wait();
我们在 ASP.NET 网络应用程序中使用 SignalR 库。代码如下所示:
服务器:
[HubName("ticketsCounterHub")]
public class MassivePrintHub : Hub
{
public void PostTicketsCount(long count)
{
Clients.All.Send(count);
}
}
public class HubFactory
{
private HubFactory() {}
public static readonly HubFactory Current = new HubFactory();
public IHubProxy GetMassivePrintHubProxy()
{
var hubConnection = new HubConnection(ConfigUtils.GetRequiredSettingValue("adminPath"));
var hubProxy = hubConnection.CreateHubProxy("ticketsCounterHub");
hubConnection.Start().Wait();
return hubProxy;
}
}
客户(JavaScript):
MassivePrintApp.controller("ListController", function ($scope, Dates) {
var hubManager = (function () {
var massivePrintHub = $.connection.ticketsCounterHub;
$.connection.hub.start();
return { massivePrintHub: massivePrintHub };
} ());
hubManager.massivePrintHub.client.Send = function (ticketsCount) {
$scope.action.Quantity = ticketsCount;
$scope.$digest();
};
});
代码的关键部分在 MVC 控制器中:
public FileResult PrintAction(int actionId, int count, DateTime actionDate, bool isThermo=false)
{
var ticketsCount = _ticketService.GetTicketsInStatusCount(actionId, actionDate, TicketStatusEnum.ToPrint);
HubFactory.Current.GetMassivePrintHubProxy().Invoke("PostTicketsCount", ticketsCount);
var stream = new MemoryStream();
xmlResponse.Save(stream);
stream.Flush();
stream.Position = 0;
return File(stream,ContentTypeEnum.XML.ToString(),String.Format("массовая {0} мероприятия {1} {2}шт.xml", isThermo?"термопечать":"печать", action.Artist,count));
}
如您所见,我们有这一行:
HubFactory.Current.GetMassivePrintHubProxy().Invoke("PostTicketsCount", ticketsCount);
这导致了问题,即每当我们调用它时,都会在 IIS 的 "Requests" 部分添加一个集线器实例。
我知道我们已经在 JavaScript 代码中启动了集线器,但我不确定如何使用现有连接或如何摆脱 HubFactory 或删除创建的集线器实例。
而且我不明白为什么集线器挂在 IIS 上。
我想从一个更简单的例子开始会对你有很大帮助。之后,您可以考虑以不同方式托管您的 SignalR 服务器(控制台应用程序或 Windows 服务),基础知识不会改变
(首次安装SignalR: NuGet: install-package Microsoft.AspNet.SignalR
)
我制作了一个简单的网络应用示例。该项目有一个 Hub class:
using Microsoft.AspNet.SignalR;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
namespace SRTest
{
public class MassivePrintHub : Hub
{
private static IHubContext hubContext = GlobalHost.ConnectionManager.GetHubContext<MassivePrintHub>();
// Can be called from your Javascript code
public void PostTicketsCount(long count)
{
Clients.All.Send(count);
}
// Can be called from your c# code
public static void Static_PostTicketsCount(long count)
{
hubContext.Clients.All.Send(count);
}
}
}
Owin 初创公司 class:
using Microsoft.AspNet.SignalR;
using Microsoft.Owin;
using Owin;
[assembly: OwinStartup(typeof(SRTest.Startup))]
namespace SRTest
{
public class Startup
{
public void Configuration(IAppBuilder app)
{
var hubConfiguration = new HubConfiguration();
hubConfiguration.EnableDetailedErrors = true;
app.MapSignalR(hubConfiguration);
}
}
}
页面(Razor 只是为了能够调用从后端调用 c# class 到 post 消息的模拟器):
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>TEST PAGE</title>
<!--Reference the jQuery library. -->
<script src='Scripts/jquery-1.6.4.js'></script>
<!--Reference the SignalR library. -->
<script src='Scripts/jquery.signalR-2.2.0.js'></script>
<!--Reference the autogenerated SignalR hub script. -->
<script src="signalr/hubs"></script>
</head>
<body>
THIS IS A TEST PAGE
<!-- Call simulator (trigger event every 5 seconds) -->
@{SRTest.SendFromBackEnd.SimulateSend();}
<script>
$(function () {
var printHub = $.connection.massivePrintHub;
// when send event happens
printHub.client.send = function (count) {
console.log("Send " + count + " tickets");
};
$.connection.hub.start().done(function () {
console.log("Connected");
});
$.connection.hub.logging = true;
});
</script>
</body>
</html>
并且我添加了一个虚拟 class,它每 5 秒通过 hubcontext 触发一次事件。
using System.Threading;
using System.Web;
namespace SRTest
{
public class SendFromBackEnd
{
public static void SimulateSend()
{
new Thread(() =>
{
Thread.CurrentThread.IsBackground = true;
while (true)
{
MassivePrintHub.Static_PostTicketsCount(2);
Thread.Sleep(5000);
}
}).Start();
}
}
}
我在 SignalR 中添加了一些日志记录,添加了一些调试点,这将帮助您了解基础知识,然后构建您计划构建的内容会容易得多。
编辑
关于挂起的请求:只要您有一个客户端通过 SSE 或 AJAX 长轮询连接到您的 SignalR 服务器,您就会有一个正在进行的请求,该请求永远不会完成。 (在 AJAX 长轮询的情况下,它会在很短的时间内完成并返回)。在我只使用 Javascript 客户端的应用程序中,如果我正在监听事件的页面打开,我只会看到请求。如果没有页面或静态页面打开则没有请求。 在我使用 .NET 客户端的应用程序中,只要这两个应用程序是 运行,并且都执行了 Sartup classes,请求将始终存在,即使没有打开页面。 (因为 .NET 客户端仍在侦听事件。)
更多信息:http://hanselminutes.com/291/damian-edwards-explains-the-realtime-web-for-aspnet-with-signalr
这是一个与线程相关的问题。像这样尝试
Task.Run(() => connection.Start().ContinueWith(task =>
{
.....
})).Wait();