loading...
Cover image for SignalR core python client (V): Reconnecting Client

SignalR core python client (V): Reconnecting Client

mandrewcito profile image Andrés Baamonde Lozano ・2 min read

Intro

These days I have been working on a new feature. The client reconnection. To know if a client has been disconnected we must set a maximum time without interaction with the socket, this will be stored on a variable that we update on the following cases:

  • last sent message
  • last received message

So, now we have a variable with the last time we send/receive information through the socket. If this time has passed we must use the socket and check the connection. This action will revalidate our timeout or close our socket.

If our socket is closed we must stop app or try to reconnect. But, we cant reconnect randomly, we need to establish a policy of reconnections avoiding the saturation of the server. On the following lines i will describe implementations of the several options i had think about.

Last message in socket

I implement an connection state checker class, that classes purpose will check and update last_message variable. It that variable reaches that threshhold we send a ping, if that ping fails socket will close.

class ConnectionStateChecker(threading.Thread):
    def __init__(
            self,
            ping_function,
            keep_alive_interval,
            sleep = 1):
        self.sleep = sleep
        self.keep_alive_interval = keep_alive_interval
        self.last_message = time.time()
        self.ping_function = ping_function
        self.runing = True

    def run(self):
        while self.runing:
            time.sleep(self.sleep)
            time_without_messages = time.time() - self.last_message
            if self.keep_alive_interval < time_without_messages:
                self.ping_function()

Strategies

For now i only implement 2 strategies, will be activated, with the parameter "type" on the with_automatic_reconnect method.

class ReconnectionType(Enum):
    raw = 0  # Reconnection with max reconnections and constant sleep time
    interval = 1  # variable sleep time

Intervals

This reconnection works with a array of 'delays' with this, an exponential reconnection will be easily implemented.


class IntervalReconnectionHandler(ReconnectionHandler):
    def __init__(self, intervals):
        self._intervals = intervals

    def next(self):
        self.reconnecting = True
        index = self.attempt_number
        self.attempt_number += 1
        return self._intervals[index]

You can generate a list of exponential delays easily.

delays = [pow(2, x) for x in range(1,10)]
# [2, 4, 8, 16, 32, 64, 128, 256, 512]  

'raw' reconnect

At 'raw reconnect' i decided to establish a limit, but if limit is no established, connections will be infinite.

class RawReconnectionHandler(ReconnectionHandler):
    def __init__(self, sleep_time, max_attempts):
        super(RawReconnectionHandler, self).__init__()
        self.sleep_time = sleep_time
        self.max_reconnection_attempts = max_attempts

    def next(self):
        self.reconnecting = True
        if self.max_reconnection_attempts is not None:
            if self.attempt_number <= self.max_reconnection_attempts:
                self.attempt_number += 1
                return self.sleep_time
            else:
                raise ValueError("Max attemps reached {0}".format(self.max_reconnection_attempts))
        else:  # Infinite reconnect
            return self.sleep_time

Client syntax

Syntax will be similar to net core signalr 3.0

hub_connection = HubConnectionBuilder()\
    .with_url(server_url)\
    .with_automatic_reconnect({
        "type": "raw",
        "keep_alive_interval": 10,  # Ping function
        "reconnect_interval": 5, # 5s between attempts
        "max_attempts": 5
    }).build()

hub_connection = HubConnectionBuilder()\
    .with_url(server_url, options={
        "access_token_factory": lambda: signalr_core_example_login(login_url, username, password)
    }).with_automatic_reconnect({
        "type": "interval",
        "keep_alive_interval": 10, # Ping function
        "intervals": [1, 3, 5, 6, 7, 87, 3]
    })\
    .build()

Links

Github
Pypi

Thank you for reading, and write any thought below :D

Posted on by:

Discussion

pic
Editor guide
 

How to get the return value of the .send function (async).
If in the Hub (signalr hub in c#), there is a function, for example public string FOO() that returns a value, how to get that value calling to
hub_connection.send("FOO", [])

.send does not seem to be ASYNC function
Thanks

 

Hi,

This functionality solves your problem? github.com/mandrewcito/signalrcore...
If does not, let me know

thank you!

 

Not sure. Is it the on_invocation parameter? I tried but did not understand how to get the return value.
Do you have an example?
Thanks!

Yes,

Here are 2 examples of using the callback. There is no return value, websocket methods are not synchronous. To listen from a callback , you must wait for a message with your invocation id.

github.com/mandrewcito/signalrcore...
github.com/mandrewcito/signalrcore...

 

How to reconnect when socket gets closed by server?

 

Hi again, i have just published a version with includes automatic reconnect, i delayed a lot, because a was fixing a problem with py2 compatibility.

pypi.org/project/signalrcore/0.7.3/

 

Reconnect is now working even after the socket closes!

When websocket re-opens after closing, I see this error:
ERROR error from callback >: Expecting value: line 1 column 1 (char 0)

Websocket stays open after this error and I am still able to receive messages through signalr. So this is probably some kind of warning.

I think that is an error related with auth, if an errors occuurs on the login function library encapsulates it an try login later:

error image

do you refer to that error?

I think that my next exteps on the library must be improving logging and documentation to prevent this kind of things.

Yes after that error, it reconnects and websocket opens again. After websocket opens, then I see this error:

"ERROR error from callback <bound method BaseHubConnection.on_message of signalrcore.hub.base_hub_connection.BaseHubConnection object at 0xb5954410: type"

That error was solved in the 0.74. Was related with the connection handshake. Yesterday I uploaded
the fix. The new version is available since then.

Awesome!
Thank you!

 

Thank you! I will test it.

 

Hi Andrés,

How can I send a signalr message everytime websocket reconnects after a disconnect?
I want to send a message to the server when I reconnect.
How can I do this?

Thank you

 

On the next version, Callbacks for on_connect and on_disconnect will be included. This change would solve your problem right?

 

Yes perfect! Thank you so much!