2016-09-15 116 views
7

在我的Web應用程序中,我想在加電時從服務器端將所有數據加載到客戶端。 之後,我希望通過Signalr管理所有通信 - 這意味着每次更新服務器都會向所有客戶端發送通知,並且他們會要求更新的數據。管理signalR通知以同步客戶端和服務器(c#)

但是,我不知道如何處理SingalR連接損壞,然後返回。我不想重新加載所有數據。我想要做的是在服務器端爲每個斷開連接的客戶端實施某種通知管理,並且每當SignalR連接再次發生時 - 向特定客戶端推送他錯過的所有通知。

在客戶端我們signalR聽衆對單的聽衆,而不是短期的生活控制器做,那這樣我們就可以防止在每個視圖變化GET請求,使應用程序更快,更方便用戶使用。因爲這種做法的,在後臺新的通知也得到處理和加工,即使它是不相關的當前視圖終端用戶是,像這樣:

// This service is initialized once only 
public class Service1 { 
     static inject = ['$rootScope'] 
    array : Item[]; 

    // This is a singleton! 
    public constructor ($rootScope){ 

     // Get all items from the server 
     GetAllItemsFromServer(); 

     // Listener for signalR updates 
     var listener = $rootScope.$on("ItemsNotificationFromServer", UpdateItems); 

     $rootScope.$on('destroy', { 
      // Stop the listener 
      listener(); 
     }) 
    } 

    // Getting all the items from the server on each controller creation 
    GetAllItemsFromServer(){ 
     // Getting the items 
    } 

    // Handle the notification from the server 
    public UpdateItems(event, result) : void 
     //.. 
    } 
} 

在例如發生了什麼的時刻當最終用戶刷新瀏覽器(F5)時,我無法知道此客戶端在連接問題期間錯過了哪些SignalR通知,因此我再次從服務器加載所有數據(很糟糕)。

爲了防止它,我想實現像這樣的 -

namespace MapUsersSample 
{ 
    public class UserContext : DbContext 
    { 
     // All those are cleaned when server is powered up 
     public DbSet<Connection> Connections { get; set; } 
     public DbSet<Notification> Notifications {get; set;} 
    } 

    public class Connection 
    { 
     [Key] 
     [DatabaseGenerationOptions.None] 
     public string ConnectionID { get; set; } 
     public bool Connected { get; set; } 

     // I fill this when disconnected 
     public List<Notification> MissedNotifications {get; set;} 

     public Connection(string id) 
     { 
      this.ConnectionID = id; 
      this.Connected = true; 
      this.MissedNotifications = new List<Notification>(); 
     } 
    } 

    public abstract class Notification() 
    { 
     public int Id {get; set;} 
     public DateTime CreationTime {get; set;} 
    } 

    .. // Many notifications implement this 
} 

public class MyHub : Hub 
{ 
    private readonly DbContext _db; 
    public class MyHub(DbContext db) 
    { 
     this._db = db; 
    } 

    // Adding a new connection or updating status to true 
    public override Task OnConnected() 
    { 
     var connection = GetConnection(Context.ConnectionId); 

     if (connection == null) 
      _db.Connections.Add(new Connection(Context.ConnectionId)); 
     else 
      connection.Connected = true; 

     return base.OnConnected() 
    } 

    // Changing connection status to false 
    public override Task OnDisconnected() 
    { 
     var connection = GetConnection(Context.ConnectionId); 

     if (connection == null) 
     { 
      Log("Disconnect error: failed to find a connection with id : " + Context.ConnectionId); 
      return; 
     } 
     else { 
      connection.Connected = false; 
     } 
     return base.OnDisconnected(); 
    } 

    public override Task OnReconnected() 
    { 
     var connection = GetConnection(Context.ConnectionId); 

     if (connection == null) 
     { 
      Log("Reconnect error - failed to find a connection with id : " + Context.ConnectionId); 
      return; 
     } 
     else { 
      connection.Connected = true; 
     } 

     // On reconnect, trying to send to the client all the notifications that he has missed 
     foreach (var notification in connection.MissedNotifications){ 
      Clients.Client(connection.ConnectionID).handleNotification(notification); 
     } 

     return base.OnReconnected(); 
    } 

    // This method is called from clients that receive a notification 
    public clientNotified(int connectionId, int notificationId) 
    { 
     // Getting the connection 
     var connection = GetConnection(connectionId); 

     if (connection == null){ 
      Log("clientNotified error - failed to find a connection with id : " + Context.ConnectionId); 
      return; 
     } 

     // Getting the notification that the client was notified about 
     var notificationToRemove = _dbConnection.Notifications.FirstOrDefault(n => n.Id == notificationId); 

     if (notificationToRemove == null) 
     { 
      Log("clientNotified error - failed to find notification with id : " + notificationId); 
      return; 
     } 

     // Removing from the missed notifications 
     connection.MissedNotifications.Remove(notificationToRemove); 
    } 

    private Connection GetConnection(int connectionId) 
    { 
     return _db.Connections.find(connectionId); 
    } 


} 

// Notifications outside of the hub 
public class Broadcaster 
{ 
    DbContext _db; 
    public Broadcaster(DbContext db) 
    { 
     _hubContext = GlobalHost.ConnectionManager.GetHubContext<MoveShapeHub>(); 
     _dbConnection = db; 
    } 

    public void NotifyClients(Notification notification) 
    { 
     var openConnections = _db.Connections.Where(x => x.Connected); 
     var closedConnections = _db.Connections.Where(x => !x.Connected); 

     // Adding all notifications to be sent when those connections are back 
     foreach (var connection in closedConnections){ 
      connection.MissedNotifications.add(notification); 
     } 

     // Notifying all open connections 
     foreach (var connection in openConnections){ 
      _hubContext.Clients.Client(connection.ConnectionID).handleNotification(notification); 
     } 
    } 
} 


client side java script: 

handleNotification(notification){ 
    hubProxy.Server.clientNotified(hub.connection.id, notification.Id) 

    // Keep handling the notification here.. 
} 

我還沒有到尚未測試,但我提出這個想法,我的團隊之前,是這種做法受歡迎?還沒有看到有人採取這種方法,我想知道爲什麼?這裏有風險嗎?

+0

在我看來,如果最終用戶刷新頁面,由JS構建的整個城堡,包括呼叫客戶端集合,SignalR連接對象都會丟失。在這種情況下,如果需要,您需要獲取所有數據,否則您將擁有的僅僅是增量數據。 –

+0

你考慮過像RabbitMQ這樣的隊列嗎? –

+0

一旦套接字連接丟失,就無法知道同一客戶端是否重新連接,除非您保持某種狀態,您還必須小心並將正確的數據推送給正確的用戶。我會建議將任何新的請求視爲新的請求。 –

回答

2

目前發生的情況是,當最終用戶刷新瀏覽器(F5)時,我無法知道此客戶端在連接問題期間錯過了哪些SignalR通知,因此我從服務器加載所有數據一遍又一遍(很糟糕)。

按F5刷新瀏覽器是一個硬重置,所有現有的SignalR連接都會丟失。新的連接將被用來獲取數據。當SignalR發現http連接出現問題時,會發生連接問題。由於暫時的網絡問題。瀏覽器刷新不是連接問題,這是用戶故意重新創建新連接的行爲。

因此,您的管理未接通知的代碼僅適用於signalR連接問題。我不認爲它適用於瀏覽器刷新,但它是一個新的連接,所以你沒有錯過任何東西。

2

你應該檢查數據是否是實際的。 它可以是最後一次更改的哈希或日期時間。

當客戶端重新連接時,應將實際數據散列或上次更改的日期時間發送給客戶端。

例如

{ 
clients: '2016-05-05T09:05:05', 
orders: '2016-09-20T10:11:11' 
} 

而且客戶端應用程序將決定它是否需要更新的數據。

在客戶端上,您可以將數據保存到LocalStorage或SessionStorage。