2015-04-24 2 views
2

Я рассматриваю архитектуру для сообщений в реальном времени между приложениями (хостинг (веб-сайт) и установленными клиентами Windows). Поскольку клиент Windows установлен в помещении клиента, мы не будем контролировать брандмауэр, то есть открывать любые порты.Гарантированная доставка сообщений

Поэтому я подумал о том, чтобы использовать SignalR для отправки уведомлений об этом, используя http через websocket или технологию возврата. Наш клиент Windows в настоящее время использует .Net 4.0 Framework.

Я сделал некоторое исследование о гарантированной доставке сообщения через signalr, и люди предложили подтвердить сообщение с 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; } 
    } 
} 

Пожалуйста, сообщите пожалуйста?

ответ

0

Мне удалось сделать это за несколько шагов.

  1. сохраняется сообщения в хранилище с Id
  2. на посыле/Connect, получить все необработанные сообщения и отправить их
  3. на стороне клиента, отправьте подтверждение на сервер и удалить сообщения из хранилище

По дизайну 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; } 
} 

Вот второй подход (а лучше один, на мой взгляд). У вас есть клиентский сингл 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.

+0

Когда клиент A отправляет сообщение клиенту B, клиент A будет вызывать метод отправки, поэтому мне нужно добавить сообщение для репо и получить все сообщение и отправить клиенту из репо. если у вас есть несколько минут, вы можете обновить свой код, чтобы я мог лучше понять его, и я не беспокоюсь о том, что клиент получает повторяющиеся сообщения из-за потери подтверждения, и важно, чтобы они не потеряли никаких сообщений. – user1754675