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

Heartbeat & Keepalive

~13 min · management, heartbeat, ping

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

TCP keepalive 로 부족한 이유

TCP 자체 keepalive 있는데, OS default 가 분-시간 단위, 프로세스별 튜닝 거의 불가. application-level ping/pong 은 우리 조건으로 — 30초마다 ping, 5초 안에 pong 기대, 안 오면 close.

조용한 half-open connection 감지

session 중간 노트북 lid 닫힘, 폰이 wifi 에서 cellular 로 switch, NAT timeout: 다 서버쪽 socket 을 send 가 여전히 성공한 듯 보이는 (커널 buffer 가 byte 받음) 상태로 남겨. OS 가 결국 timeout 할 때까지. heartbeat 가 분이 아니라 초에 잡아.

서버 vs 클라 heartbeat

어느 쪽이 driving 해도 ok. 서버 driven (서버 ping, 클라 pong) 이 더 단순 — 서버에 타이머 하나. 클라 driven (클라 ping, 서버 pong) 은 멍청한 클라 (IoT, battery 빡센 모바일) 가 다른 거 안 일어날 때 ping skip 가능. 하나 골라 protocol 에 문서화.

Code

서버 driven heartbeat·python
import asyncio, time

class HeartbeatManager:
    def __init__(self, *, interval=30, timeout=5):
        self.interval = interval
        self.timeout = timeout
        self.last_pong: Dict[WebSocket, float] = {}

    async def run(self, ws: WebSocket):
        self.last_pong[ws] = time.time()
        try:
            while True:
                await asyncio.sleep(self.interval)
                if ws.client_state.value != 1:  # 1 == CONNECTED
                    return
                await ws.send_json({'type': 'ping'})
                # Give the client `timeout` seconds to pong.
                await asyncio.sleep(self.timeout)
                age = time.time() - self.last_pong.get(ws, 0)
                if age > self.interval + self.timeout:
                    await ws.close(code=4000, reason='heartbeat timeout')
                    return
        finally:
            self.last_pong.pop(ws, None)

    def handle_pong(self, ws: WebSocket):
        self.last_pong[ws] = time.time()

heartbeat = HeartbeatManager()

@app.websocket('/ws')
async def with_heartbeat(websocket: WebSocket):
    await websocket.accept()
    asyncio.create_task(heartbeat.run(websocket))
    try:
        async for msg in websocket.iter_json():
            if msg.get('type') == 'pong':
                heartbeat.handle_pong(websocket)
                continue
            # ... handle other messages
    except WebSocketDisconnect:
        pass

External links

Exercise

Echo 서버에 heartbeat manager 박아. 클라 connect 후 kill -STOP 으로 frozen 클라 시뮬. 35초 (interval + timeout) 안에 서버가 code 4000 으로 close 해야 함. heartbeat 없으면 같은 죽은 클라가 분 단위로 살아있을 수 있어.

Progress

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

댓글 0

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

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