This commit is contained in:
Víðir Valberg Guðmundsson 2023-05-17 22:21:10 +02:00
parent 08c5ccc68a
commit 4341a02203
1 changed files with 50 additions and 0 deletions

View File

@ -330,6 +330,56 @@ And that's it! We can now open two browser windows and see the messages appear i
Check out the repo for the full code where I've also added a simple form for submitting new messages.
### Dealing with reconnections
One of the nice things about SSE is that it will automatically reconnect if the connection is lost. It even has a
mechanism for dealing with the fact that the client might have missed some events while it was disconnected.
This is done by sending a `Last-Event-ID` header with the request. The value of this header is the `id` of the last
event that the client received. The server can then use this to determine which events to send to the client.
To deal with this we can expand on our `stream_messages` function and view as follows:
:::python
async def stream_messages(last_id: int | None = None) -> AsyncGenerator[str, None]:
connection_params = connection.get_connection_params()
connection_params.pop('cursor_factory')
aconnection = await psycopg.AsyncConnection.connect(
**connection_params,
autocommit=True,
)
channel_name = "lobby"
if last_id:
messages = ChatMessage.objects.filter(id__gt=last_id)
async for message in messages:
yield f"id: {message.id}\nevent: message_created\ndata: {message.as_json()}\n\n"
async with aconnection.cursor() as acursor:
await acursor.execute(f"LISTEN {channel_name}")
gen = aconnection.notifies()
async for notify in gen:
payload = json.loads(notify.payload)
event = payload.get("event")
event_id = payload.get("event_id")
data = payload.get("data")
yield f"id: {event_id}\nevent: {event}\ndata: {data}\n\n"
async def stream_messages_view(
request: HttpRequest,
) -> StreamingHttpResponse:
last_id = request.headers.get("Last-Event-ID")
return StreamingHttpResponse(
streaming_content=stream_messages(last_id=last_id),
content_type="text/event-stream",
)
We now send the `id` of each message, and whenever a (re)connection is made we check if the client sent a `Last-Event-ID`
and if so we send all messages with an `id` greater than that.
This change does also require some changes to our utility functions and model. Those are to be found in the git repo.
### Conclusion
Django is boring, which is a good thing, to the degree where it is always the safe option. But with the advances in