Chapter 9: Real-Time Systems & Background Tasks
FastAPI enables high-performance real-time communication via WebSockets and provides a mechanism for non-blocking post-response processing through Background Tasks. These tools are essential for building responsive, event-driven architectures that maintain low latency for the end-user while handling complex side-effects asynchronously.
I. WebSocket Engineering: Full-Duplex Connectivity
WebSockets provide a persistent, bi-directional communication channel over a single TCP connection. In FastAPI, WebSockets are first-class citizens that utilize the ASGI send and receive primitives to handle data frames without the overhead of HTTP headers.
@app.websocket("/ws/{client_id}")
async def websocket_endpoint(websocket: WebSocket, client_id: int):
await websocket.accept() # 101 Switching Protocols handshake
try:
while True:
# Low-level: Awaits raw ASGI 'websocket.receive' event
data = await websocket.receive_text()
await websocket.send_text(f"Message received: {data}")
except WebSocketDisconnect:
# Handle cleanup (remove from connection manager)
pass
Low-Level Technical Insight: The Handshake
A WebSocket connection begins as an HTTP request with an Upgrade: websocket header. The FastAPI layer inspects the ASGI scope['type']. If it is 'websocket', the request-response lifecycle is terminated, and the TCP socket remains open. Bi-directional communication is then achieved through Binary or Text Frames as defined by RFC 6455.
II. Background Tasks: Post-Response Orchestration
BackgroundTasks allow engineers to schedule functions to execute after the HTTP response has been sent. This is ideal for side-effects that do not impact the user experience, such as sending confirmation emails, updating search indexes, or logging analytics.
III. Production Anti-Patterns
- In-Memory WebSocket Managers: Storing active connections in a simple
list[]in a multi-worker production environment. A client on Worker 1 cannot communicate with a client on Worker 2. Use Redis Pub/Sub to synchronize state across workers. - Long-Running Background Tasks: Using
BackgroundTasksfor CPU-intensive work (e.g., video transcoding). These tasks run within the worker process and can degrade HTTP throughput. Use Celery or RabbitMQ for heavy asynchronous workloads. - Missing Heartbeats: Failing to implement "Ping/Pong" frames. Most cloud load balancers (like AWS ALB) will kill idle WebSocket connections after 60 seconds.
IV. Performance Bottlenecks
- Broadcast Fan-out Latency: Iterating through thousands of active connections and awaiting
send_text()sequentially. Useasyncio.gather()with concurrency limits to broadcast efficiently. - Context Switching Overhead: High-frequency small messages (e.g., real-time mouse tracking) can overwhelm the
asyncioevent loop with thousands of micro-tasks per second. - Memory Leakage: Failing to remove disconnected clients from the manager, leading to an ever-growing list of "Zombie" sockets that the Garbage Collector cannot reclaim.