Как правильно обрабатывать события бэкэнд-сервиса в SignalR? - PullRequest
0 голосов
/ 28 февраля 2020

У меня есть некоторые сомнения относительно следующего кода, который реализует конечную точку SignalR, которая получает и отправляет сообщения.

В основном ISendValuesService создает новые значения и вызывает событие NewValueRegistered всякий раз, когда это происходит. После этого события концентратор SignalR отправляет сообщение всем подключенным клиентам, которые зарегистрировались для идентификатора значения.

Однако само сообщение не является проблемой.

Проблема заключается в том, что клиенты могут вызывать SubscribeToVariables и UnsubscribeFromVariables во время выполнения. Для каждого из этих вызовов создается новый объект-концентратор (в соответствии с дизайном SignalR). Когда это происходит, новый концентратор также подписывается на событие, и любые новые значения будут отправляться клиентам несколько раз, поскольку теперь их несколько

. В настоящее время я ограничил регистрацию событием со значением * 1049. * переменная bool (_eventRegistered в приведенном ниже коде), но это выглядит неуклюже.

Есть ли рекомендуемый шаблон для обработки такого случая для сигнала без пропуска повторной регистрации события в последующих экземплярах концентратора?

public class ValueHub : Hub
{
    private ISendValuesService _sendValuesService;
    private IHubContext<ValueHub> _hubContext;
    private static bool _eventRegistered;
    private object _eventRegistrationLock = new object();

    public ValueHub(ISendValuesService sendValuesService, IHubContext<ValueHub> hubContext)
    {
        _hubContext = hubContext;

        _sendValuesService = sendValuesService;

        lock (_eventRegistrationLock)
        {
            if (!_eventRegistered)
            {
                _sendValuesService.NewValueRegistered += OnNewValueRegistered;
                _eventRegistered = true;
            }
        }
    }

    private void OnNewValueRegistered(object sender, NewValueRegisteredEventArgs e)
    {
        _ = Task.Run(() => HandleNewValueTask(e));
    }

    private void HandleNewValueTask(NewValueRegisteredEventArgs e)
    {
        var valueMsg = $"\"ID\":{e.Id},\"Value\":\"{e.Value,3}\",\"Timestamp\":\"{e.RegistrationTime}\"";

        Parallel.ForEach(_sendValuesService.Subscriptions,
            new ParallelOptions() { MaxDegreeOfParallelism = 4 },
            subscription =>
            {
                if (subscription.Value.Contains(e.Id))
                {
                    _hubContext.Clients.Client(subscription.Key).SendCoreAsync("NewValue", new[] { valueMsg });
                }
            });
    }

    public async Task SubscribeToVariables(IEnumerable<int> variableIdsToRegister)
    {
        //Save the connectionId since the Context might be disposed before the task is started
        var connectionId = Context.ConnectionId.ToString();

        Debug.WriteLine($"Register variables for connection {connectionId}.");

        await Task.Run(() =>
                       _sendValuesService.SubscribeToVariables(connectionId, variableIdsToRegister)
                    ); 
    }

    public async Task UnsubscribeFromVariables(IEnumerable<int> variableIdsToUnregister)
    {
        //Save the connectionId since the Context might be disposed before the task is started
        var connectionId = Context.ConnectionId.ToString();

        Debug.WriteLine($"Unregister variables for connection {connectionId}.");

        await Task.Run(() =>
                       _sendValuesService.UnsubscribeFromVariables(Context.ConnectionId, variableIdsToUnregister)
                    );
    }

    protected override void Dispose(bool disposing)
    {
        base.Dispose(disposing);
    }
}

Таким образом, следующее происходит, если логическое значение не присутствует.

  1. Клиент подключается - экземпляр концентратора 1; обработчик событий, зарегистрированный впервые (отныне одно сообщение отправляется клиенту при запуске события)

  2. вызовы клиентов Subscribe - Hub instance 2; обработчик события регистрируется второй раз (с этого момента клиенту отправляются два сообщения при запуске события)

  3. вызовы клиентов Unubscribe - Hub instance 3; обработчик событий регистрируется в третий раз (с этого момента клиенту отправляются три сообщения при запуске события)

  4. вызовы клиентов Subscribe - экземпляр Hub 4; Обработчик событий регистрируется четвертый раз (с этого момента клиенту при запуске события отправляются четыре сообщения)

Я также пытался отписаться в методе Dispose концентратора, но это не удалось либо сработайте.

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

Я добавил PO C в качестве репозитория Github. : https://github.com/msdeibel/SignalRIssue/tree/master

...