Best Practices for reconnecting a disconnected SignalR client (JS)
Asked Answered
S

1

8

I'd like to improve the resilience of my clientside implementation of a signalR client.

Currently, I do this:

hub.server.sendClientNotification(string, appSettings.username());

However occasionally, an exception related to connectivity is thrown because either the server is not responding or the client's internet connection has become unusable.

I'd like to solve this by queuing up requests and then doing something like this:

try {
    // pop from queue
    hub.server.sendClientNotification(string, appSettings.username());
    // success - discard element
} catch (e) {
    // requeue element
}

With this kind of implementation, would I need to re-initialize my signalR connection using $.connection.hub.start between disconnects, or can I just continue attempting hub transmits within an interval?

This is what I'm proposing:

var hub = null;

const internalQueue = [];

const states = {
    connecting: 0, connected: 1, reconnecting: 2, disconnected: 4
}

const signalrModule = {};

var isInitialized = false;

const connectSignalR = function () {
    return new Promise(function (resolve, reject) {
        if ($.connection.hub.state == states.connected) {
            resolve();
        } else {
            window.hubReady = $.connection.hub.start({ transport: ["serverSentEvents", "foreverFrame", "longPolling"] });
            window.hubReady.done(function () {
                isInitialized = true;
                resolve();
            });
            window.onbeforeunload = function (e) {
                $.connection.hub.stop();
            };
        }
    })
}

signalrModule.init = function (handleNotification) {
    hub = $.connection.appHub;
    hub.client.clientNotification = handleNotification;
    $.connection.hub.qs = {
        "username": appSettings.username()
    };
    connectSignalR();
}

const tryEmptyQueue = function () {
    connectSignalR().then(function() {
        if (isInitialized) {
            var continueTrying = true;
            while (internalQueue.length && continueTrying) {
                const nextMessage = internalQueue.shift();
                try {
                    hub.server.sendClientNotification(nextMessage, appSettings.username());
                } catch (e) {
                    internalQueue.push(nextMessage);
                    continueTrying = false;
                }
            }
        }
    })
}

signalrModule.sendClientNotification = function (message) {
    if (typeof message != "string") {
        message = JSON.stringify(message);
    }
    if (isInitialized) {
        try {
            hub.server.sendClientNotification(message, appSettings.username());
            tryEmptyQueue();
        } catch (e) {
            logger.log("SignalR disconnected; queuing request", logger.logLevels.warning);
            internalQueue.push(message);
        }
    } else {
        internalQueue.push(message);
    };
}

const internalQueueInterval = setInterval(function () {
    tryEmptyQueue();
}, 5000);

return signalrModule;
Sale answered 30/7, 2017 at 0:39 Comment(0)
B
10

Follow the documentation

In some applications you might want to automatically re-establish a connection after it has been lost and the attempt to reconnect has timed out. To do that, you can call the Start method from your Closed event handler (disconnected event handler on JavaScript clients). You might want to wait a period of time before calling Start in order to avoid doing this too frequently when the server or the physical connection are unavailable. The following code sample is for a JavaScript client using the generated proxy.

    $.connection.hub.disconnected(function() {
   setTimeout(function() {
       $.connection.hub.start();
   }, 5000); // Restart connection after 5 seconds.
});
Bluenose answered 31/7, 2017 at 12:11 Comment(2)
Using this approach, will the reconnected client be seen as the same client and will it receive any queued messages sent to it while disconnected?Suannesuarez
The user will be connected with a new connectionID. Personally, I don't rely on connectionID, rather I use Groups and when they reconnect they are added back to their own Group or Groups. I don't believe queued messages will be received, at least not my experience. If you stored messages to be sent and flag them based on a user viewing them, that is a possibility but that is a whole other topic.Bluenose

© 2022 - 2024 — McMap. All rights reserved.