This problem would be best addressed by the future Channel Messaging specification, which has not been implemented by any browsers to-date, but I managed to solve it by limiting the number of connections as described by Alex Ford and using localStorage
as a message bus between tabs.
The storage
event lets you propagate data between tabs while keeping a single SignalR connection open (thereby preventing connection saturation). Calling localStorage.setItem('sharedKey', sharedData)
will raise the storage
event in all other tabs (not the caller):
$(window).bind('storage', function (e) {
var sharedData = localStorage.getItem('sharedKey');
if (sharedData !== null)
console.log(
'A tab called localStorage.setItem("sharedData",'+sharedData+')'
);
});
You can test if ($.connection.hub.state === 1)
to determine if a given tab should notify the other tabs via localStorage (courtesy of Alex) to prevent duplicate localStorage.setItem
calls.
Facebook overcomes this browser limitation by serving persistent connections over several sub-domains, but this can complicate deployment and testing.
Caveats
Old Connections: In Alex's solution, you need to be careful of Disconnect()
not being called (e.g. exception), and you filling up your HubConnections
bucket (or repository) with old hub connections. If the Session ID does not change (can happen), this may prevent new clients from establishing a SignalR connection even though none are active. Alternatively, timestamp new connections and have a sliding expiration to minimise potential impact.
Locking: localStorage
may be subject to race conditions as it does not implement any locking as described here.
To support different types of events, you should encode an eventType in your JSON messages and test for it on the storage
event.
Fallbacks
If a SignalR connection cannot be established, I fall back onto polling the server every 45 seconds to retrieve a notification count.
If you don't want to use localStorage, you can use cookies, but it's not as clean.
与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…