私のWebアプリケーションでは、起動時にサーバからすべてのデータをクライアント側にロードします。 その後、私はすべてのコミュニケーションを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によって構築された城全体が失われます。その場合、必要に応じてすべてのデータを取得する必要があります。そうでない場合は、増分データだけが必要になります。 –
あなたはRabbitMQのようなキューを考えましたか? –
ソケット接続が失われてしまった場合、状態を維持しない限り、同じクライアントが再接続されるかどうかを知る方法がないため、正しいデータを慎重に押して正しいユーザーにプッシュする必要があります。私は新しいリクエストを新しいリクエストとして扱うことをお勧めします。 –