C.W.K.
Stream
Lesson 05 of 07 · published

Graceful Shutdown

~11 min · management, lifespan, shutdown

Level 0Poller
0 XP0/60 lessons0/10 achievements
0/120 XP to next level120 XP to go0% complete

uvicorn 재시작 시 일어나는 일

아무 것도 안 하면 모든 WebSocket connection 이 재시작 시 code 1006 (abnormal closure) 으로 끝나. 클라가 network error 보고; 재연결 logic 발동; 다 ok. 근데 더 잘할 수 있어: system.reconnect message 보내서 클라한테 알리고, 1001 (going away) 로 깔끔히 close.

FastAPI lifespan

lifespan context manager 사용. yield 후 돌아가는 모든 게 shutdown. 모든 활성 WebSocket close 후 return — uvicorn 이 lifespan return 까지 exit 안 해.

gather 로 동시 close

10,000 connection 하나씩 close 하면 분 걸려. asyncio.gather(*close_tasks, return_exceptions=True) 로 fan out + 동시 wait. return_exceptions=True 가 나쁜 클라 하나 때문에 나머지 abort 안 되게.

Code

lifespan 관리하는 shutdown·python
from contextlib import asynccontextmanager
from fastapi import FastAPI
import asyncio

@asynccontextmanager
async def lifespan(app: FastAPI):
    yield  # serve

    # Shutdown phase
    log.info('draining %d WebSocket connections', manager.total_connections)
    # 1. Tell clients we're going.
    for room in list(manager.rooms):
        await manager.broadcast(room, {
            'type': 'system.reconnect',
            'data': {'reason': 'server-restart'},
        })

    # 2. Brief grace period so clients receive the message.
    await asyncio.sleep(1.0)

    # 3. Close all connections concurrently.
    close_tasks = [
        ws.close(code=1001, reason='server-restart')
        for ws in list(manager.ws_meta)
    ]
    await asyncio.gather(*close_tasks, return_exceptions=True)
    log.info('all WebSocket connections drained')

app = FastAPI(lifespan=lifespan)

External links

Exercise

위 lifespan 서버에 추가. SIGTERM 보내; 클라가 system.reconnect message 그 다음 깔끔한 1001 close 봐야 함. lifespan 없으면 1006 cold 봐.

Progress

Progress is local-only — sign in to sync across devices.
이 페이지에서 버그를 발견하셨거나 피드백이 있으세요?문제 신고

댓글 0

🔔 답글 알림 (로그인 필요)
로그인댓글을 남기려면 로그인해 주세요.

아직 댓글이 없어요. 첫 댓글을 남겨보세요.