반응형

<출처> https://sairamkrish.medium.com/handling-server-send-events-with-python-fastapi-e578f3929af1

FastAPI, push 기능 (Server sent events) 구현 

 

Aug 6, 2020

Server sent events with Python FastAPI

In asynchronous server side flows, we like to keep the client informed about the progress or change in state. Many a times, we may choose to go with polling mechanisms. But it has it’s own disadvantages.

Let’s discuss an alternative approach — Server-sent events and how we can implement this with FastAPI backend

Server sent events

As an example, let’s take a look into fastapi based backend endpoint. Here we have a status endpoint which sends server events.

FastAPI is based on starlette. To help us in this flow, we will depend on a small library called sse_starlette

pip install sse-starlette

Below example shows a streaming endpoint status/stream

from sse_starlette.sse import EventSourceResponse
from fastapi import APIRouter, Request
from app.utils import status_event_generator
...
router = APIRouter()
@router.get('/status/stream')
async def runStatus(
        param1: str,
        request: Request
):
    event_generator = status_event_generator(request, param1)
    return EventSourceResponse(event_generator)
  • We return an EventSourceResponse which takes in a generator.
  • A sample generator logic for server sent event explained below.

Event generator

import asyncio
'''
Get status as an event generator
'''
status_stream_delay = 5  # second
status_stream_retry_timeout = 30000  # milisecond
async def status_event_generator(request, param1):
    previous_status = None
    while True:
        if await request.is_disconnected():
            logger.debug('Request disconnected')
            break
        if previous_status and previous_status['some_end_condition']:
            logger.debug('Request completed. Disconnecting now')
            yield {
                "event": "end",
                "data" : ''
            }
            break
        current_status = await compute_status(param1)
        if previous_status != current_status:
            yield {
                "event": "update",
                "retry": status_stream_retry_timeout,
                "data": current_status
            }
            previous_status = current_status
            logger.debug('Current status :%s', current_status)
        else:
            logger.debug('No change in status...')
        await asyncio.sleep(status_stream_delay)

Client side

An example client side logic which consume a server sent event endpoint and act based on events:

const evtSource = new EventSource("http://localhost:8080/status/stream?param1=test");
evtSource.addEventListener("update", function(event) {
    // Logic to handle status updates
    console.log(event)
});
evtSource.addEventListener("end", function(event) {
    console.log('Handling end....')
    evtSource.close(); 
});
  • Now we can handle how client should respond to different server events easily
  • end condition is very important. Without this, client will reconnecting to server, even if server has successfully completed previous request.
Debugging server sent events directly in Chrome
Different attributes of a MessageEvent
  • In Chrome, we can do various experiments on server sent events. Analyzing the behaviour of an EventStream in browser is a good way to start.
  • We can also observe, how server side tries to keep the connection alive with ping request and what type of events are emitted from server.
  • We can also ensure connection is getting closed as expected when the criteria is satisfied.
  • Also observe if server restarts or server ends the connection, browser will try to re-establish the connection. Till we close the connection on client side, it’s going to be alive.

HTTP Connection Behaviour

  • Browser tries to keep this connection alive. Even if server side closes the connection, browser will try to reconnect, unless event source is closed.
  • This is useful to keep the connection alive even in case of intermittent network issue or server went down etc.,
  • If browser is closed, server can sense this and stop sending any more events. This optimizes server side resources

Conclusion

There is a limit on maximum number of live connections that can be maintained for a browser — domain combination. So using server sent events efficiently and closing the connection properly plays a key role while working on this flow. Please check the browser compatibility required for the application and whether it supports SSE. Many client-server flows that uses frequent polling can be updated with server sent events, if the problem in hand matches what SSE can provide.

 
반응형

+ Recent posts