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
Thank you for reading, and write any thought below :D
Top comments (15)
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 tohub_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:
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!