SignalR получает все больше и больше сообщений после обновления страницы - PullRequest
1 голос
/ 05 марта 2020

Я написал в ASP.NET веб-приложение, которое обменивается данными с C# программой через SignalR. Здесь соответствующие части моего кода. Конечно, если я забыл что-то важное для решения моей проблемы, пожалуйста, спросите в комментариях.

ASP. NET

HubClientManager.cs

using Microsoft.AspNet.SignalR.Client;
using System;
using System.Threading.Tasks;
using System.Diagnostics;

namespace MyProject
{
    public class HubClientManager
    {
        public String GetServerURI()
        {
            return String.Concat("http://192.168.1.103", ":8080", "/signalr");
        }

        public IHubProxy GetHub()
        {
            var hubConnection = new HubConnection(GetServerURI());
            var hub = hubConnection.CreateHubProxy("liveHub");

            try
            {
                var task = hubConnection.Start();
                Task.WhenAll(task).Wait();
            }
            catch (Exception e)
            {
                Debug.WriteLine(e);
            }
            return hub;
        }
    }
}

Index.cs html

<script src="@ViewBag.serverURI/hubs"></script>
<script>
    $(function () {   
        $.connection.hub.url = "@ViewBag.serverURI";
        var hub = $.connection.liveHub;

        hub.client.orderStatus = function (opened, suspended, running, closed, cancelled) {
            $("#cntOpened").text(opened);
            $("#cntSuspended").text(suspended);
            $("#cntRunning").text(running);
            $("#cntClosed").text(closed);
            $("#cntCancelled").text(cancelled);
        };

        $.connection.hub.logging = true;

        $.connection.hub.start().done(function () { });

        $.connection.hub.error(function (error) {
            console.log('SignalR error: ' + error)
        });
    });
</script>

MachinesController.cs

public class MachinesController : Controller
{
    private HubClientManager _manager = new HubClientManager();
    private IHubProxy _hub;

    public MachinesController()
    {
        _hub = _manager.GetHub();
    }

    // GET: Machines
    public ActionResult Index()
    {
        ViewBag.serverURI = _manager.GetServerURI();
        return View(...);
    }

    ...
}

C#

Hub.cs

namespace MyProject.Hubs
{
    public class LiveHub : Hub
    {
        private readonly Erp _erp;

        public LiveHub(Erp erp)
        {
            _erp = erp;
            _erp.HubSendOrderStatus += Erp_HubSendOrderStatus;
        }

        private void Erp_HubSendOrderStatus(int arg1, int arg2, int arg3, int arg4, int arg5)
        {
            Clients.All.orderStatus(arg1, arg2, arg3, arg4, arg5);
        }

        ...    

        public override Task OnConnected()
        {
            _erp.SendOrderStatus();       
            return base.OnConnected();
        }
    }
}

Все отлично работает: я могу показать данные на веб-странице, поступающей из приложения C# (из класса Erp, и отправить Команды с веб-страницы обратно в приложение движка. Здесь я сообщил только о небольшом наборе функций, но их должно быть достаточно, чтобы понять, что происходит.

Возможно, я слепой и не вижу своей ошибки, глядя на примеры. Во всяком случае, каждый раз, когда я обновляю sh страницу в браузере или даже загружаю другую страницу того же приложения (которая, конечно, использует тот же код JavaScript, приведенный выше), я получаю все больше и больше SignalR сообщений! Я имею в виду, если в первый раз я получаю сообщение orderStatus каждые 10 секунд, после перезагрузки (или изменения страницы ) Я получаю 2 сообщения каждые 10 секунд. Еще один refre sh, и они становятся 3, и так далее ... через некоторое время вся система перестает работать, потому что она получает тысячи сообщений одновременно.

Я знаю, что есть обратный вызов OnDisconnected(), но кажется, он вызывается фреймворком для уведомления , что клиент отключился (и мне это не интересно).

ОБНОВЛЕНИЕ

Я не могу опубликовать весь текст * Класс 1046 *, потому что он длиной 3k + строк ... во всяком случае, большая часть кода выполняет все другие функции (базы данных, полевые коммуникации и т. Д. c ...). Здесь единственные функции, связанные с концентратором:

public event Action<int, int, int, int, int> HubSendOrderStatus;

public void SendOrderStatus()
{
    using (MyDBContext context = new MyDBContext())
    {
        var openedOrders = context.Orders.AsNoTracking().Count(x => x.State == OrderStates.Opened);
        var suspendedOrders = context.Orders.AsNoTracking().Count(x => x.State == OrderStates.Suspended);
        var runningOrders = context.Orders.AsNoTracking().Count(x => x.State == OrderStates.Running || x.State == OrderStates.Queued);
        var closedOrders = context.Orders.AsNoTracking().Count(x => x.State == OrderStates.Completed);
        var cancelledOrders = context.Orders.AsNoTracking().Count(x => x.State == OrderStates.Cancelled);

        HubSendOrderStatus?.Invoke(openedOrders, suspendedOrders, runningOrders, closedOrders, cancelledOrders);
    }
}

public async Task ImportOrdersAsync()
{
    // doing something with I/O file
    SendOrderStatus();
}

public void JobImportOrders()
{
    Timer t = null;
    t = new Timer(
    async delegate (object state)
    {
        t.Dispose();
        await ImportOrdersAsync();
        JobImportOrders();
    }, null, 10 * 1000, -1);
}

public Erp()
{
    // initialize other stuff
    JobImportOrders();
}

EDIT

AutofacContainer.cs

public class AutofacContainer
{
    public IContainer Container { get; set; }
    public AutofacContainer()
    {
        var builder = new ContainerBuilder();
        var config = new HubConfiguration();
        builder.RegisterHubs(Assembly.GetExecutingAssembly()).PropertiesAutowired();
        builder.RegisterType<Erp>().PropertiesAutowired().InstancePerLifetimeScope();
        Container = builder.Build();
        config.Resolver = new AutofacDependencyResolver(Container);
    }
}

Ответы [ 2 ]

0 голосов
/ 07 марта 2020

Следуя советам "Alex - Tin Le", я обнаружил, что обработчики не были удалены с помощью:

_erp.HubSendOrderStatus -= Erp_HubSendOrderStatus;

Я исправил эту странную проверку поведения, если обработчик уже зарегистрирован:

if (!_erp.HubSendOrderStatus_isRegistered())  _erp.HubSendOrderStatus += Erp_HubSendOrderStatus;

Функция HubSendOrderStatus_isRegistered выглядит следующим образом:

public bool HubSendOrderStatus_isRegistered()
{
    return HubSendOrderStatus != null;
}

Я решил исходную проблему : больше нет потока сообщений при установлении новых соединений.

Последний бит, который я не понимаю, это то, почему он отправляет 2 сообщения одновременно независимо от количества активных соединений . Отладка как Javascript, так и кода сервера Я заметил, что новое соединение установлено на $.connection.hub.start().done(function () { });, как и ожидалось. Но другой был уже создан при обновлении страницы, даже до любой доступной точки останова. Но удаление «явного» приводит к тому, что сообщения не принимаются.

0 голосов
/ 05 марта 2020

Можете ли вы попробовать это?

_erp.HubSendOrderStatus -= Erp_HubSendOrderStatus;
_erp.HubSendOrderStatus += Erp_HubSendOrderStatus;

Обычно я всегда делаю это, чтобы избежать многократной регистрации одного и того же обработчика.

...