Как работают вебсокеты
Что проверяют
Понимаете ли вы, что постоянное соединение не ложится на синхронную модель WSGI, и умеете ли назвать конкретный стек: ASGI, event loop, библиотеку.
Как отвечать
- Рукопожатие: HTTP-запрос GET с заголовками
Upgrade: websocketиSec-WebSocket-Key, ответ 101 Switching Protocols сSec-WebSocket-Accept(SHA-1 от ключа и фиксированного GUID из RFC 6455). Дальше то же TCP-соединение работает на двусторонний обмен кадрами. - Кадры: текстовые, бинарные, управляющие (ping, pong, close). Клиент обязан маскировать свои кадры, сервер нет.
- В Python вебсокеты живут только в ASGI: WSGI (Flask на синхронных воркерах Gunicorn) их не поддерживает в принципе, нужен uvicorn или hypercorn. Варианты: библиотека
websockets,WebSocketиз FastAPI и Starlette, django-channels,aiohttp. - Каждое соединение это долгоживущая корутина. Блокирующий вызов внутри неё (
requests, синхронный драйвер БД,time.sleep) останавливает весь event loop и подвешивает остальных клиентов: нужны асинхронные драйверы илиasyncio.to_thread.
from fastapi import FastAPI, WebSocket, WebSocketDisconnect
app = FastAPI()
@app.websocket("/ws")
async def ws(sock: WebSocket):
await sock.accept()
try:
while True:
msg = await sock.receive_text()
await sock.send_text(f"echo: {msg}")
except WebSocketDisconnect:
passГде спотыкаются
Держат список подключений в глобальной переменной и запускают несколько воркеров uvicorn. Каждый процесс видит только свои соединения, часть клиентов рассылку не получает: нужен Redis pub/sub или брокер.
Не ловят WebSocketDisconnect и оставляют висящие задачи: счётчик соединений растёт, память течёт, а в логах это выглядит безобидно.
Передают токен как получится. Браузерный WebSocket не умеет ставить заголовок Authorization, поэтому токен кладут в subprotocol или в query, а Origin сервер проверяет сам.
Забывают про ping и pong. Прокси рвут простаивающее соединение примерно за минуту, и без keepalive клиент молча перестаёт получать данные.