C.W.K.
Stream
Lesson 06 of 08 · published

Connection Manager 패턴

~13 min · fastapi, manager, broadcast

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

Handler 하나로 부족

Echo 서버는 connect 한 클라랑만 말해. 더 흥미로운 거 — chat, broadcast, presence — 는 서버가 *여러* connection 을 동시에 알아야 해. 패턴은 connection manager 도입: 열린 socket 추적하는 작은 singleton, application 이 신경쓰는 거 (room, user, channel) 로 indexed.

최소 manager

room 이름을 WebSocket 객체 set 에 매핑하는 dict 면 대부분 케이스 커버. 직접 메시징 필요하면 user-id 추적 추가. observability 필요하면 metadata (connected_at, ip) 추가. 각 추가가 점진적.

Broadcast 중 죽은 connection 처리

room 의 set 을 broadcast 위해 iterate 할 때쯤 일부 socket 은 이미 half-dead (network drop, 미감지). 모든 send 호출 wrap; dead ref 모으고; loop 후 drop. iteration 중 set mutate 금지.

Code

최소 connection manager·python
from fastapi import FastAPI, WebSocket, WebSocketDisconnect
from typing import Dict, Set

app = FastAPI()

class ConnectionManager:
    def __init__(self):
        self.rooms: Dict[str, Set[WebSocket]] = {}

    async def connect(self, websocket: WebSocket, room: str):
        await websocket.accept()
        self.rooms.setdefault(room, set()).add(websocket)

    def disconnect(self, websocket: WebSocket, room: str):
        members = self.rooms.get(room)
        if not members:
            return
        members.discard(websocket)
        if not members:
            self.rooms.pop(room, None)  # garbage collect empty rooms

    async def broadcast(self, room: str, message: dict):
        dead = []
        for ws in self.rooms.get(room, set()):
            try:
                await ws.send_json(message)
            except Exception:
                dead.append(ws)
        for ws in dead:
            self.disconnect(ws, room)

manager = ConnectionManager()

@app.websocket('/ws/{room}')
async def chat(websocket: WebSocket, room: str):
    await manager.connect(websocket, room)
    try:
        async for msg in websocket.iter_json():
            await manager.broadcast(room, {
                'type': 'chat.message',
                'data': msg,
            })
    except WebSocketDisconnect:
        manager.disconnect(websocket, room)
        await manager.broadcast(room, {
            'type': 'user.left',
            'data': {'room': room},
        })

External links

Exercise

위 manager 짜. /ws/general 에 브라우저 탭 세 개 열어. 한 탭에서 타이핑하면 다른 두 탭에 message 나타나는 거 확인. 한 탭 crash 하면 나머지 두 탭이 user.left broadcast 받고, 마지막 탭 닫으면 room dict 가 cleanup 되는 거 확인.

Progress

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

댓글 0

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

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