在我的web应用程序中,我想从服务器上加载所有数据到客户端。在那之后,我希望所有的通信都通过Signalr进行管理-这意味着每次更新服务器都会向所有客户端发送通知,并且他们会请求更新的数据。
但是,当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..
}
我还没有测试它,但是在我向我的团队提出这个想法之前,这个方法受欢迎吗?我没见过有人采用这种方法,我想知道为什么?这里有风险吗?
目前发生的事情,例如,当终端用户刷新浏览器(F5),我不知道这个客户端在连接问题期间错过了什么SignalR通知,所以我从服务器重新加载所有数据(它很糟糕)。
按F5刷新浏览器是硬复位,所有现有的SignalR连接将丢失。将建立新的连接来获取数据。连接问题发生在SignalR注意到http连接问题的情况下,例如由于临时网络问题。浏览器刷新不是连接问题,这是用户故意重新创建新连接的行为。
所以,你管理错过通知的代码只适用于signalR连接问题。我不认为它会在浏览器刷新时工作,但它是一个新的连接,所以你没有错过任何东西。
您应该检查数据是否真实。可以是Hash或datetime。
当客户端重新连接时,您应该发送实际的数据哈希值或最后更改的日期时间给客户端。
例如
{
clients: '2016-05-05T09:05:05',
orders: '2016-09-20T10:11:11'
}
客户端应用程序将决定需要更新哪些数据。