如何使用SignalR保证消息传递?

我正在使用C#和SignalR开发实时客户端 – 服务器应用程序。 我需要尽快向客户发送消息。 我在服务器上的代码:

for (int i = 0; i < totalRecords; i++) { hubContext.Clients.Client(clientList[c].Key).addMessage( serverId, RecordsList[i].type + RecordsList[i].value); Thread.Sleep(50); } 

如果有延迟> = 50 ms,一切正常,但如果没有延迟或延迟小于50 ms,则会丢失一些消息。 我需要尽可能快地发送消息。 我想我需要检查是否收到消息,并且只在发送另一个消息之后。
如何以正确的方式做到这一点?

SignalR不保证消息传递。 由于SignalR在调用客户端方法时不会阻塞,因此您可以非常快速地调用客户端方法。 不幸的是,客户端可能并不总是准备好在发送消息后立即接收消息,因此SignalR必须缓冲消息。

一般来说,SignalR每个客户端最多可缓冲1000条消息。 一旦客户端落后超过1000条消息,它将开始丢失消息。 可以增加1000的DefaultMessageBufferSize,但这会增加SignalR的内存使用量,但仍然无法保证消息传递。

http://www.asp.net/signalr/overview/signalr-20/performance-and-scaling/signalr-performance#tuning

如果您想保证邮件传递,您必须自己确认。 您可以按照建议仅在确认上一条消息后发送消息。 如果等待每条消息的ACK太慢,您也可以一次确认多条消息。

我最近遇到了同样的问题。 我想出的答案是创建一个out消息队列系统。

而不是立即发送消息,将它们排队并让后台线程/定时器发送消息。 为了使其通用,您可以为每个发送消息传递新的GUID ID。 然后,您将该消息(id和与之关联的数据)存储在列表中。

这是一些示例代码。 lock应该由ReaderWriterLockSlim或更高效的其他东西替换,但这使您可以很好地了解从哪里开始。

总的来说,它不是非常有效,它是一个如何处理需要订购的消息传递的快速草案。

 void Main() { using(var mq = new MessageQueue()) { var m1 = new Message((m)=>{ Console.WriteLine("a"); }); var m2 = new Message((m)=>{ Console.WriteLine("b"); }); mq.QueueMessage(m1); mq.QueueMessage(m2); var r = new Random(); while(mq.StillWaiting) { Thread.Sleep(1); var rnum = r.Next(0,100); if(rnum > 75) { var nm = mq.NextMessage; if(nm != null) mq.ResponseReceived(nm.messageId); } } } } // Define other methods and classes here public class MessageQueue : IDisposable{ public List Messages {get;set;} public bool StillWaiting { get{ lock(Messages) return Messages.Count > 0; } } public Message NextMessage{ get{ lock(Messages) return Messages.FirstOrDefault(); } } private Timer _sendTimer; private List responses; public MessageQueue() { Messages = new List(); responses = new List(); _sendTimer = new Timer(timerTick,this,10,10); } public void ResponseReceived(Guid id) { lock(Messages) { if(responses.Contains(id) == false) responses.Add(id); } } public void QueueMessage(Message m) { lock(Messages) Messages.Add(m); } public void timerTick(object state) { lock(Messages) { foreach(var m in Messages.ToArray()) { Console.WriteLine("Checking " + m.messageId); if(m.SendCount == 0 || responses.Contains(m.messageId) == false) { m.Send(); return; } Console.WriteLine("Got response from " + m.messageId); Messages.Remove(m); responses.Remove(m.messageId); } } } public void Dispose() { _sendTimer.Dispose(); } } public class Message { public Guid messageId; public int SendCount {get;private set;} private Action sendAction; public Message(Action a) { SendCount = 0; messageId = Guid.NewGuid(); sendAction = a; } public void Send() { sendAction.Invoke(this); SendCount++; } }