WebSockets를 10만 연결로 확장하기: 실시간 크리켓 앱에서 얻은 교훈

발행: (2026년 4월 25일 AM 06:39 GMT+9)
6 분 소요
원문: Dev.to

Source: Dev.to

초기 시도와 한계

  • 설정: socket.io를 실행하는 Node.js 프로세스 하나; 연결된 모든 클라이언트가 모든 실시간 경기 구독.
  • 성능:
    • 2 k 동시 연결 – 정상 작동.
    • 15 k 연결 – 하트비트가 떨어지기 시작.
    • 40 k 연결 – 이벤트 루프 지연이 3 초를 초과; 재연결 폭풍이 상황을 악화시킴.

시사점: 단일 Node 프로세스는 이벤트 루프가 수행하는 다른 작업에 따라 20 k–40 k 소켓 사이에서 한계에 도달한다. 단일 프로세스에서 모든 클라이언트에게 브로드캐스트하는 비용은 **O(N)**이며, 인기 경기 하나가 전체 루프를 잡아먹는다. 재연결 폭풍은 실제이다: 게이트웨이를 재시작하면 끊어진 모든 클라이언트가 ~2 초 안에 다시 연결을 시도해 자체 DDoS를 만든다.

핵심 교훈

  1. 무상태 게이트웨이 – WebSocket 노드는 “멍청하게” 연결만 유지하고 비즈니스 로직을 두지 않는다.
  2. Redis Pub/Sub 버스match_id를 키로 하는 Redis 채널을 사용하고, 각 게이트웨이는 구독 후 로컬에서 팬아웃한다.
  3. 스티키 세션 – ALB 수준의 스티키 세션(쿠키 기반)을 사용해 클라이언트를 동일 게이트웨이에 고정시켜 상태 스러싱을 방지한다.

아키텍처 재설계

score provider → ingest worker → Redis PUB match:123
               ↘ N gateways SUB match:123 → WS push to clients
  • 수평 확장: 게이트웨이 노드를 추가하면 Redis가 모든 노드에 팬아웃한다.
  • 단일 Redis 클러스터는 초당 수십만 건의 pub/sub 메시지를 처리할 수 있다.

메시지 최적화

  • 델타 메시지만 전송한다.
{ "over": 14.3, "runs": 4, "batsman": "Kohli" }
  • 4 KB 전체 스냅샷을 보내는 대신 200 바이트 델타를 보내면, 120 k 연결 기준 게이트웨이당 outbound 대역폭이 ~480 MB/s에서 ~24 MB/s로 감소한다. 이는 필요한 인스턴스 크기를 크게 낮춘다.

느린 클라이언트 처리

  • 2G 모바일 클라이언트는 각 메시지를 ACK하는 데 8 초가 걸릴 수 있다.
  • 규칙: 클라이언트가 5 초 이내에 ACK하지 않으면 가장 오래된 대기 메시지를 삭제하고 "resync" 이벤트를 보낸다. 클라이언트는 이후 REST 엔드포인트를 통해 전체 스코어카드를 가져와 WebSocket을 재개한다.
  • 이는 작은 UX 불편을 서버 안정성과 OOM 충돌 방지라는 큰 이득으로 교환한다.

그레이스풀 재시작 및 배포

  • 게이트웨이가 재시작될 때 각 클라이언트의 재연결 지연에 0–5 초의 무작위 jitter를 추가한다.
  • 서버 측에서는 게이트웨이를 그레이스풀하게 배출한다: ALB가 새로운 연결을 받지 않게 하고, 기존 연결은 현재 메시지를 마무리한 뒤 프로세스가 종료된다.
  • 롤링 배포가 사건이 아니라 일상이 된다.

상태 모니터링

실시간 시스템이 건강한지 판단하는 세 가지 지표:

MetricDesired Threshold
Event‑loop lag (p99)Tip: 처음부터 uWebSockets.js를 사용하라 — socket.io에 비해 원시 WebSocket 처리량이 약 5배 더 효율적이다. 로드‑셰딩 메커니즘을 일찍 구축하라: “commentary”(댓글)와 같은 저우선순위 이벤트를 “wicket”(위켓)과 같은 고우선순위 이벤트보다 먼저 드롭한다.

결론

실시간 스포츠, 협업 편집, 거래 플랫폼, 실시간 대시보드 등 어떤 분야이든 WebSocket 확장은 날카로운 가장을 가진 discipline이다. 이 영역에서 개발한다면, Xenotix Labs는 인도 경기 당일 트래픽을 견디는 실시간 스택을 이미 제공하고 있다. .

0 조회
Back to Blog

관련 글

더 보기 »

데이터베이스 병목 현상

“It was fast… until users showed up.” “That’s what I told a friend when we were debugging his system.” 문제 모든 요청이 데이터베이스에 의존했다. 각각 …