在我的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..
}
我還沒有到尚未測試,但我提出這個想法,我的團隊之前,是這種做法受歡迎?還沒有看到有人採取這種方法,我想知道爲什麼?這裏有風險嗎?
在我看來,如果最終用戶刷新頁面,由JS構建的整個城堡,包括呼叫客戶端集合,SignalR連接對象都會丟失。在這種情況下,如果需要,您需要獲取所有數據,否則您將擁有的僅僅是增量數據。 –
你考慮過像RabbitMQ這樣的隊列嗎? –
一旦套接字連接丟失,就無法知道同一客戶端是否重新連接,除非您保持某種狀態,您還必須小心並將正確的數據推送給正確的用戶。我會建議將任何新的請求視爲新的請求。 –