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