保证邮件传递

时间:2015-04-24 09:01:55

标签: c# signalr rabbitmq

我正在研究应用程序(托管(Web)和已安装的Windows客户端)之间的实时消息的体系结构设计。由于Windows客户端安装在客户端,我们无法控制防火墙,即打开任何端口。

所以我想到使用SignalR通过websocket或fallback技术使用http发送实例通知。我们的Windows客户端目前正在使用.Net 4.0 Framework。

我做了一些关于通过信号器确保消息传递的研究,人们建议用GUID确认消息,但不确定如何实现这个想法。此外,当客户端未连接时,我需要在RabbitMQ上排队消息,而onConnected只是从队列中发送所有消息。

namespace SignalRHub.Hubs
{
    [Authorize]
    public class ChatHub : Hub
    {
        public void Send(string who, string data)
        {
            string name = Context.User.Identity.Name;

            List<string> groups = new List<string>();
            groups.Add(name);
            groups.Add(who);

            Message message = new Message()
            {
                messageId = Guid.NewGuid(),
                data = data
            };

            Clients.Groups(groups).addNewMessageToPage(name, JsonConvert.SerializeObject(message));
        }

        public void AcknowledgeServer(Guid messageId)
        {
            // Process the message acknowledge
            var msgGuid = messageId;
        }

        public override Task OnConnected()
        {
            string name = Context.User.Identity.Name;
            Groups.Add(Context.ConnectionId, name);

            return base.OnConnected();
        }
    }

    public class Message
    {
        public Guid messageId { get; set; }
        public String data { get; set; }
    }
}

请指教?

1 个答案:

答案 0 :(得分:0)

我只需几步即可完成此操作。

  1. 使用Id
  2. 在邮件库中保留邮件
  3. on send / connect事件,获取所有未处理的消息并发送
  4. 在客户端,向服务器发送确认并从存储库中删除消息
  5. 根据设计,signalR无法保证客户端只会收到消息只接收一次,因为即使您实现了这样的协议,您也可能会丢失确认消息并仍然会收到消息两次。

    第一种方法:服务器为所有未处理的消息发送事件触发器addNewMessages。可以做的伎俩,但如果永远不会调用发送事件...

    //just a sample repo : should be persisted, thread safe ...
    public static class MessageRepository
    {
        public static List<Message> UnprocessedMessages = new List<Message>();
    
        public static void AddMessage(Message msg)
        {
            UnprocessedMessages.Add(msg);
        }
    
        public static List<Message> GetMessagesByIssuer(string issuer)
        {
            return UnprocessedMessages.Where(m => m.Issuer.Equals(issuer)).ToList();
        }
    
        public static void Remove(Guid id)
        {
            if (UnprocessedMessages.Any(m => m.messageId.Equals(id)))
            {
                var message = UnprocessedMessages.FirstOrDefault(m => m.messageId.Equals(id));
                UnprocessedMessages.Remove(message);
            }
        }
    }
    
    
    [Authorize]
    public class ChatHub : Hub
    {
        public void Send(string who, string data)
        {
            string name = Context.User.Identity.Name;
    
            List<string> groups = new List<string>();
            groups.Add(name);
            groups.Add(who);
    
            Message message = new Message()
            {
                messageId = Guid.NewGuid(),
                data = data,
                Issuer = name,
                Receiver = who,
                CreationDate = DateTime.UtcNow
            };
    
            MessageRepository.AddMessage(message);
            var unProcessedMessages = MessageRepository.GetMessagesByIssuer(name).OrderBy(m => m.CreationDate).ToList();
    
            unProcessedMessages.ForEach(m =>
            {
                Clients.Groups(groups).addNewMessageToPage(name, JsonConvert.SerializeObject(m));
            });
    
    
        }
    
        public void AcknowledgeServer(Guid messageId)
        {
            // Process the message acknowledge
            var msgGuid = messageId;
            MessageRepository.Remove(msgGuid);
        }
    
        public override Task OnConnected()
        {
            string name = Context.User.Identity.Name;
            Groups.Add(Context.ConnectionId, name);
    
            return base.OnConnected();
        }
    }
    
    public class Message
    {
        public Guid messageId { get; set; }
        public String data { get; set; }
        public String Issuer { get; set; }
        public String Receiver { get; set; }
        public DateTime CreationDate { get; set; }
    }
    

    这是第二种方法(在我看来更好)。您有一个带有Timer的C#客户端单例,它会定期检查您的存储库,然后发送未处理的消息。您还应该删除过期的邮件。

    public class PresenceMonitor
    {
        private Timer _timer;
    
        // How often we plan to check if the connections in our store are valid
        private readonly TimeSpan _presenceCheckInterval = TimeSpan.FromSeconds(10);
    
    
        public PresenceMonitor()
        {
    
        }
    
        public void StartMonitoring()
        {
            if (_timer == null)
            {
                _timer = new Timer(_ =>
                {
                    try
                    {
                        Check();
                    }
                    catch (Exception ex)
                    {
                        // Don't throw on background threads, it'll kill the entire process
                        Trace.TraceError(ex.Message);
                    }
                },
                null,
                TimeSpan.Zero,
                _presenceCheckInterval);
            }
        }
    
        private void Check()
        {
            var context = GlobalHost.ConnectionManager.GetHubContext<ChatHub>();
            var messages = MessageRepository.GetAllMessages();
            messages.ForEach(m =>
            {
                if (context != null)
                {
                    List<string> groups = new List<string>();
                    groups.Add(m.Issuer);
                    groups.Add(m.Receiver);
    
                    context.Clients.Groups(groups).addNewMessageToPage(m.Issuer, JsonConvert.SerializeObject(m));
                }
            });
    
    
        }
    }
    

    startuc课程中,初始化Monitor