How to buffer messages on signal hub and send them when the right client appears?

随声附和 提交于 2019-12-06 15:56:34

If I understood well, you want to defer SignalR message sending by using something like a synchronized call in some IHostedService. Here is what I managed to achieve so far.

  • Your approach that consists in using a ConcurrentQueue that contains invokable Action delegates to handle the concurrent hub calls is the right one. As you mention, it has to be injected as a singleton.

So here the Queues class:

public class Queues {
    public ConcurrentQueue<Action<IHubContext<MyHub, IMyEvents>>> MessagesQueue { get; set; }
}
  • Now we need to capture the ConnectionId of the caller so a call can get an answer later. SendMessage enqueue the necessary action delegate to perform a call against a hub instance as a parameter.

As an example SendMessage will trigger an answer back to the caller, and BroadcastMessage will send a message to all clients.

Using a captured hub instance instead would lead to an exception here because the hub will be disposed, quickly. That's why it would be injected later in another class. Have a look on SendMessage_BAD

Here is the MyHub class and the corresponding IMyEvents interface:

public interface IMyEvents {
    void ReceiveMessage(string myMessage);
}

public class MyHub : Hub<IMyEvents> {
    Queues queues;

    public MyHub(Queues queues) {
        this.queues = queues;
    }

    public void SendMessage(string message) {
        var callerId = Context.ConnectionId;
        queues.MessagesQueue.Enqueue(hub => hub.Clients.Client(callerId).ReceiveMessage(message));
    }

    // This will crash
    public void SendMessage_BAD(string message) {
        this.callerId = Context.ConnectionId;
        queues.MessagesQueue.Enqueue(_ => this.Clients.Client(callerId).ReceiveMessage(message));
    }

    public void BroadcastMessage(string message) {
        queues.MessagesQueue.Enqueue(hub => hub.Clients.All.ReceiveMessage(message));
    }
}
  • Now, using a naive approach, this code will trigger the message sending a deferred way. (At work, a timer ensure a regular cadence, and the class is an IHostedService but it is does not appear here). This class has to be injected as a singleton.

Here the DeferredMessageSender class:

public class DeferredMessageSender {
    Queues queues;
    IHubContext<MyHub, IMyEvents> hub;

    public DeferredMessageSender(Queues queues, IHubContext<MyHub, IMyEvents> hub) {
        this.queues = queues;
        this.hub = hub;
    }

    public void GlobalSend() {
        while(queues.MessagesQueue.TryDequeue(out var evt)) {
            evt.Invoke(hub);
        }
    }
}

Hope it helps.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!