2016-09-15 118 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。