Hi,
I want to implement automatic reconnect for a request-reply channel
built with asyncio. The challenge here (in contrast to what was
discussed before) is, that the server is stateful and needs to detect if
a client reconnects.
Before I discuss my ideas and questions, I’d like to briefly explain the
channel’s architecture (
source code):
The UI is a Channel object that a client creates when it connects to
a server and that a server creates for each new connection.
A client sends a request to the server and waits for a reply with
rep
= await channel.send('ohai')
The server side channel waits for requests and replies to them with
req = await channel.recv()
req.reply('cya')
=> Complete exampleThe Channel object wraps a ChannelProtocol which is very similar to the
built-in asyncio streaming protocol (except that my messages are not
"lines" but length-terminated JSON objects). I don’t use custom
transport implementations.
When a client reconnects, both the client's and the server's channel
instances need to stay the same. Creating a new protocol instance (if
needed) after a reconnect should be no problem, because it is not
exposed to the user.
I think the only way for the server to detect if a new client connects
or an existing client reconnects is that the client uses some kind of
session ID. The server associates a session ID with a channel. And
reuses channel instances for known SIDs and creates new channels else.
This means that a Channel will always send a greeting message to the
server after it successfully connected. It also needs to send
a goodby-message when it disconnects on purpose (to tell the server
to close the session/channel).
Since every message already has a message type (REQUEST, RESULT,
EXCEPTION), it wouln’t hurt to add two more types (SESSION_START,
SESSION_STOP).
So my first question is: Does this sound reasonable?
What I described above should work well when the connections drops
*before* a request is made or *between* a request and its reply. But
what happens if a disconnect happens while a request or reply is being
transmitted (after the user called channel.send() but before the server
received the complete message).
Is there a way to detect this? Or should the client use a timeout and
resent the same message (with the same message ID) after a timeout
occurs? The server could then decide whether to execute the message
(when the original message got lost on the way from the client to the
server) or just re-sent its reply (when the reply got lost on its way
from the server back to the client).
And how could I test this? Its relatively easy to provoke disconnects
before a request or between a request and reply, but I have now idea how
to break the connection exactly when a message is being transfered.
Cheers,
Stefan