Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to deploy Blazor server in production and overcome SignalR re-connection problems?

Blazor server is a fantastic technology, but it constantly breaks with SignalR not being able to reconnect to the server.

How would one fix this in production? I'm having people leaving their laptops in sleep or putting phone with website for a 5 seconds away, than - "Attempting to reconnect".

And always failed. Users are waiting just to see "Reload" button.

Hot to overcome this issue and force reconnect SignalR, even if website was not active in mobile browsers or browsers of sleeping pc?

like image 507
idchlife Avatar asked Feb 03 '20 00:02

idchlife


1 Answers

Overview

Blazor has built in options for customising the startup process, but they are not really documented very well.

This is it as far as I can tell: Configure the SignalR client for Blazor Server apps

However there are more options, including the ability to set re-connection options.

Sample

In the code sample below, I am setting maxRetries and retryIntervalMilliseconds (lines 48/49) - which define how aggressively the client will try to reconnect in the event of a communication breakdown.

I also configure a custom reconnectionHandler (line 51) with a minimal implementation that will attempt to reconnect (without showing any UI) and if all attempts fail, it will simply reload the page.

This is not a particularly clever thing to do, but it works as a sample to demonstrate how you might go about customising the process.

Further Reading AspNetCore Circuits Client Side Code

** PLEASE DO NOT USE THIS SAMPLE IN PRODUCTION - IT IS ILLUSTRATIVE ONLY **

First, turn off the Blazor boot process with autostart="false"
<script autostart="false" src="_framework/blazor.server.js"></script>
Then, you can provide your own connection handler and settings
<script>
    async function connectionDown(options) {
        console.log("Connection Down - you could do some UI here...");
        for (let i = 0; i < options.maxRetries; i++) {
            console.log("Waiting for reconnect attempt #"+(i+1)+" ...");
            await this.delay(options.retryIntervalMilliseconds);
            if (this.isDisposed) {
                break;
            }

            try {
                // reconnectCallback will asynchronously return:
                // - true to mean success
                // - false to mean we reached the server, but it rejected the connection (e.g., unknown circuit ID)
                // - exception to mean we didn't reach the server (this can be sync or async)
                console.log("Starting Reconnect attempt #"+(i+1)+" ...");
                const result = await window.Blazor.reconnect();
                if (!result) {
                    // If the server responded and refused to reconnect, log it 
                    console.error("Server Rejected");
                } else {
                    // Reconnected!
                    return;
                }
            } catch (err) {
                // We got an exception so will try again 
                console.error(err);
            }
        }
        // all attempts failed - let's try a full reload
        // This could be a UI change instead or something more complicated
        location.reload();
    }

    function delay(durationMilliseconds) {
        return new Promise(resolve => setTimeout(resolve, durationMilliseconds));
    }

    function connectionUp(e) {
        // Reconnected
        console.log("Connection UP!");
        // if you have a UI to hide/change you can do that here.
    }

    window.Blazor.start({
        reconnectionOptions: {
            maxRetries: 30,
            retryIntervalMilliseconds: 500,
        },
        reconnectionHandler: {
            onConnectionDown: e => connectionDown(e),
            onConnectionUp: e => connectionUp(e)
        }
    });
</script>
like image 94
Mister Magoo Avatar answered Nov 15 '22 07:11

Mister Magoo