Socket.IO 서버 벤치마킹
Source: Dev.to
Source: https://example.com/your-article
설정
경쟁자
| 라벨 | 런타임 | WebSocket 서버 |
|---|---|---|
| node‑ws | Node.js 24.11.1 | ws |
| node‑uws | Node.js 24.11.1 | uWebSockets.js v20.52.0 |
| bun‑ws | Bun 1.3.6 | ws |
| bun‑native | Bun 1.3.6 | @socket.io/bun-engine 0.1.0 |
*ws*는 기본값입니다. 순수 JS이며 안정적이지만 빠르지는 않습니다(스포일러: 빠르지 않음).
테스트 서버는 최근 프로젝트 Versus Type 의 백엔드를 약간 수정한 버전입니다—실시간 PvP 타이핑 게임. 인증, 레이트‑리밋, DB 호출을 제거했습니다.
로드 제너레이터로는 Artillery 와 artillery-engine-socketio-v3 플러그인을 사용해 수천 명의 동시 클라이언트가 WebSocket으로 연결해 게임을 플레이하도록 시뮬레이션합니다.
하드웨어
| 역할 | 인스턴스 | 사양 |
|---|---|---|
| 서버 | AWS Standard B2als v2 | 2 vCPU, 4 GB RAM, Ubuntu 22.04 LTS |
| 공격자 | AWS Standard B4als v2 | 4 vCPU, 8 GB RAM, Ubuntu 22.04 LTS |
공격 흐름
- Artillery가 초당 4명의 가상 사용자를 생성합니다.
- 각 사용자는
/api/pvp/matchmake를 호출합니다. - 서버는 매치메이킹 알고리즘을 실행하고 룸 ID를 반환합니다(룸당 최대 6명).
- 사용자는 WebSocket으로 연결해 방에 입장하고 게임 상태(텍스트)를 받습니다.
- 서버가 카운트다운을 방송하고, 플레이어들은 0이 될 때까지 대기합니다.
- 사용자는 분당 60타(≈ 1 event / 200 ms) 속도로 키 입력 이벤트를 전송합니다.
- 각 키 입력마다 서버는 입력을 검증하고, 상태를 업데이트한 뒤 방에 있는 모든 사람에게 새로운 상태를 방송합니다.
- 사용자는 지연 시간을 측정하기 위해 1초마다 ping 이벤트를 보냅니다.
텍스트는 벤치마크가 끝날 때까지 게임이 종료되지 않도록 충분히 길게 설정했습니다.
(실제 서버는 또한 시스템 메시지와 매초 WPM 업데이트를 방송합니다.)
GitHub 저장소: (서버, 클라이언트 및 결과 데이터 포함).
Source:
결과
전체 승자
Node + uWS (파란선) 가 모든 지표에서 메모리 사용량을 제외하고 다른 모든 구성보다 우수했으며, 메모리 사용량에서는 Bun이 앞섰다.
0 – 800 클라이언트
- Bun 서버는 이벤트 루프 지연이 (~0 ms) 로 Node 서버에 비해 현저히 낮다.
node‑uws가 가장 안정적이다.ws서버들 (Bun 및 Node 모두) 은 p95 지연이 15‑20 ms 로 상승한다.- 나머지 두 구성은 ≈ 5 ms 로 견고하게 유지된다.
800 – 1 500 클라이언트
node‑ws가 폭발적으로 지연이 증가한다 – 약 1 k 클라이언트에서 급증.bun‑ws와bun‑native도 뒤따르지만, 시점이 늦다.- 이벤트 루프 지연도 동일한 패턴을 보인다.
- CPU 사용량은
node‑ws가 ~1 k 클라이언트에서 100 %,bun‑ws가 ~1.2 k,bun‑native가 ~1.3 k에서 100 %에 도달한다. node‑uws는 ≈ 80 % CPU 를 유지하며 1.5 k 클라이언트에서도 비슷한 비율로 상승한다.- 처리량은
node‑uws를 제외하고는 모두 불안정해진다. - 메모리:
node‑uws가 이상하게 감소했다가 다시 상승하는 모습을 보이며, Bun 서버들은 전체적으로 메모리를 적게 사용한다 – Bun 의 메모리 관리가 인상적이다.
핵심: node‑ws 는 부하를 감당하지 못하고, node‑uws 는 평탄한 지연과 낮은 이벤트 루프 지연을 유지한다.
1 500 – 2 100 클라이언트
node‑ws,bun‑ws,bun‑native는 사실상 죽은 상태가 되며 – 지연이 천정부지로 상승한다.node‑uws는 여전히 ≈ 80 % CPU 로 일정한 낮은 지연을 유지한다.- 참고:
node‑ws의 p95 지연이 잠시 평탄하게 보이는 이유는 메트릭 기록이 중단돼서이며, Artillery 의 pushgateway 가 새로운 값이 도착하기 전까지 마지막 값을 계속 보여준다.
2 100 – 3 300 클라이언트
node‑uws가 꾸준히 버텨낸다 – CPU 가 80 % 수준을 유지하고 지연도 낮으며 서버가 응답성을 유지한다.- 다른 모든 구성은 압도당 – CPU 가 100 %에 달하고 지연이 크게 폭증하며 빈번히 충돌한다.
Source: …
Takeaways
| Configuration | Strengths | Weaknesses |
|---|---|---|
| node‑uws | 전반적인 성능 최고, 안정적인 지연시간, 낮은 이벤트‑루프 지연, ~80 % CPU로 3 k 이상 클라이언트 처리 가능 | Bun보다 약간 높은 메모리 사용량 |
| bun‑native | 메모리 사용량이 매우 적고, 클라이언트 수가 적을 때 낮은 이벤트‑루프 지연 | 높은 클라이언트 수를 지속할 수 없으며, ~1.3 k 클라이언트 이후 지연시간이 급격히 상승 |
| bun‑ws | 초기 단계에서 낮은 이벤트‑루프 지연, 좋은 메모리 사용량 | 부하가 걸리면 bun‑native보다 일찍 실패 |
| node‑ws | 간단하고 기본 옵션 | 약 1 k 클라이언트에서 충돌, CPU와 지연시간이 높음 |
Bottom line: 고트래픽 실시간 애플리케이션에서 순수 성능이 필요하다면 Node + uWebSockets.js가 명확한 승자입니다. 메모리가 주요 고려사항이고 Bun에 익숙하다면 Bun 네이티브 엔진이 괜찮은 2차 선택이 될 수 있지만, 확장성 한계에 주의해야 합니다.

`node-uws` is the only one still standing. It's at ~90‑100 % CPU now.
Throughput starts to become less stable, and latency slowly creeps up. It goes dead after ~3 250 clients.
We can say it could handle a solid 3 000‑3 100 concurrent clients just fine—more than double the next best (`bun-native`).
Full Graph
CSV 파일은 GitHub 여기에서 확인할 수 있습니다.
Bun, What Happened?
It’s a surprise to see bun-native get absolutely destroyed here, because the Bun WebSocket server uses uWebSockets under the hood.
I don’t exactly know the reason why, but it might be because @socket.io/bun-engine is still very new (v0.1.0) and may have inefficiencies and abstraction layers that add overhead.




