Upd.
This commit is contained in:
parent
08c5ccc68a
commit
4341a02203
|
@ -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
|
||||
|
|
Loading…
Reference in a new issue