피드로 돌아가기
고전 돌아보기, C10K 문제 (C10K Problem)
올리브영 테크블로그올리브영 테크블로그
Backend

고전 돌아보기, C10K 문제 (C10K Problem)

C10K 문제 해결을 위해 Apache httpd의 prefork/worker 방식을 버리고 epoll() 기반의 non-blocking I/O로 전환해 동시 접속자 증가 시에도 RPS 성능 저하를 방지

2023년 11월 30일12intermediate

Context

1999년 Dan Kegel이 제기한 C10K 문제(동시 사용자 1만명 접속)에서 전통적인 socket 프로그래밍 방식(accept 후 프로세스 fork 또는 thread 생성)은 심각한 성능 저하를 야기했다. Apache httpd의 prefork/worker 방식은 동시 접속자가 증가할수록 RPS(Request per Second)가 급격히 감소하는 문제를 노출했다.

Technical Solution

  • 기존 프로세스 fork 기반 구조를 epoll() 기반 non-blocking I/O 방식으로 전환: Linux Kernel 2.6부터 지원되는 epoll()을 사용해 1만 개 소켓을 하나의 프로세스에서 효율적으로 관리
  • BSD의 kqueue() 기술을 리눅스로 포팅한 epoll() 도입: 동시 다중 이벤트를 단일 프로세스에서 처리하는 메커니즘으로 전환
  • Java 환경에서는 JDK 4부터 도입된 NIO(Non-blocking I/O) 기술 활용: Non-blocking I/O와 Zero copy 기능 제공
  • Netty 프레임워크를 통한 고성능 I/O 추상화: Hadoop, Kafka 같은 I/O 집약적 프로젝트가 NIO 기반으로 구현
  • Node.js의 이벤트 루프 아키텍처: epoll()을 기반으로 비동기 처리 구현
  • nginx 웹 서버 채택: epoll() 또는 select(), kqueue()를 기반으로 구현되어 동시 접속자 증가 시에도 RPS 성능 유지
  • Apache httpd 2.4 이후 Non-blocking I/O 지원 추가: httpd 2.2까지는 prefork/worker만 지원했으나 2.4부터 non-blocking 옵션 제공

Key Takeaway

C10K 문제 해결에서 핵심은 thread/process 기반의 1대1 연결 모델을 버리고 epoll() 같은 OS 레벨의 multiplexing 기술을 채택한 것이며, 이러한 기술 진화가 현대의 Node.js, nginx, Kafka 같은 고성능 시스템의 기반이 되었다는 점에서 오래된 문제 해결이 기술 발전의 원동력임을 보여준다.


동시 사용자 대규모 처리가 필요한 Java 서비스 개발 시 thread-per-connection 방식을 피하고 NIO(Selector 기반) 또는 Netty 프레임워크를 도입하면 메모리 스택 오버헤드(JDK 64bit에서 thread당 1MB)와 context switching 비용을 극적으로 감소시킬 수 있으며, Node.js나 nginx를 선택하면 이벤트 루프와 epoll() 기반의 non-blocking I/O가 자동으로 적용되어 추가 최적화 없이도 높은 동시성을 확보할 수 있다.

원문 읽기