如何在信号中心上缓冲消息并在合适的客户端出现时发送消息?

时间:2019-04-17 20:17:49

标签: c# asp.net-core signalr asp.net-core-signalr

我有两种类型的客户端连接我的signalR服务器(ASP.NET Core)。其中一些是发送者,一些是接收者。我需要将消息从发送者路由到接收者,这不是问题,但是当没有接收者时,我需要以某种方式缓冲消息而不丢失它们(最好是单例类中的ConcurrentQueue),但是当在第一个接收者连接时,消息缓冲区需要开始出队。哪个是最好的方法?

我创建了单例类,该类包装了arround ConcurrentQueue集合,并在其中使消息入队和出队。另外,我还有一个单独的单例类,用于持久保存接收者的connectionIDs。我在第二个类中实现了事件,该事件在接收者列表为空之后在第一个接收者连接时触发事件,但这也许不是一个好方法,我不知道如何在Hub中使用id,因为存在多个实例signalR集线器。 第二种方法是将持久性类标记为控制器,并在该类中注入ContextHub和消息缓冲区,然后从该类中出队缓冲区,然后直接将消息发送给接收者。

1 个答案:

答案 0 :(得分:0)

如果我很好理解,您想通过在某些IHostedService中使用同步调用之类的方法来延迟SignalR消息的发送。到目前为止,这是我设法实现的目标。

  • 您的方法是使用一个ConcurrentQueue包含正确的方法,该ConcurrentQueue包含可调用的Action委托来处理并发的中心调用。如您所述,它必须作为单例注入。

这里是Queues类:

public class Queues {
    public ConcurrentQueue<Action<IHubContext<MyHub, IMyEvents>>> MessagesQueue { get; set; }
}
  • 现在我们需要捕获呼叫者的ConnectionId,以便以后可以得到呼叫答复。 SendMessage排队必要的操作委托以对作为参数的集线器实例执行调用。

例如,SendMessage将触发对呼叫者的应答,而BroadcastMessage将向所有客户端发送一条消息。

使用捕获的集线器实例会导致异常,因为集线器将被快速处置。这就是为什么它将在以后的其他课程中注入的原因。看看SendMessage_BAD

这是MyHub类和相应的IMyEvents接口:

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));
    }
}
  • 现在,使用幼稚的方法,此代码将触发消息以延迟方式发送。 (在工作中,计时器确保常规节奏,并且该类是IHostedService,但此处未出现)。此类必须作为单例注入。

这里是DeferredMessageSender类:

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

希望有帮助。