이벤트 기반 프로그래밍으로 대규모 동시 연결 처리: C++에서의 실전 경험
카테고리
프로그래밍/소프트웨어 개발
서브카테고리
웹 개발
대상자
- 대상: C++ 네트워크 서버 개발자, 고성능 서버 아키텍처 설계자
- 난이도: 중급 이상 (시스템 콜, 블로킹/논블로킹 I/O, 동시성 관리 이해 필요)
핵심 요약
- C10K 문제는 10,000개 동시 연결 처리를 위한 근본적 아키텍처 한계를 의미하며, 이벤트 기반 프로그래밍이 해결책임.
- epoll/kqueue 시스템 콜을 통해 I/O 멀티플렉싱을 구현하여, thread-per-connection 모델 대신 이벤트 루프를 사용해야 함.
- ConnectionState 구조체와 shared_ptr + mutex를 사용한 스레드 안전한 연결 상태 관리가 핵심.
- HTTP 헤더 파싱은 "folding", "Content-Length 누락" 등 복잡한 경계 조건을 고려해야 함.
섹션별 세부 요약
1. Thread-per-Connection 모델의 한계
- 200개 동시 연결 시 메모리 사용량 폭증 및 CPU 스레드 전환 비용으로 서버가 완전히 연결 거부하게 됨.
- C10K 문제는 실무에서 반드시 고려해야 할 서버 확장성 한계로, thread-per-connection 모델은 이를 극복할 수 없음.
- 이벤트 기반 프로그래밍은 스레드 대신 I/O 멀티플렉싱을 통해 동시 연결을 효율적으로 처리.
2. 이벤트 기반 프로그래밍과 I/O 멀티플렉싱
- epoll (Linux)과 kqueue (macOS/BSD)는 이벤트 중심의 I/O 감지 기법으로, edge-triggered 모드로 최적화됨.
EPOLLET
플래그를 사용해 데이터 상태 변화만 감지하며, 한 번에 모든 데이터 읽기가 필요함.- kqueue는
EVFILT_READ
등 필터 기반으로 이벤트 등록, API 차이를 추상화한EventNotifier
클래스 구현.
3. 이벤트 루프 구현
EventLoop::run()
메서드는 1초 타임아웃으로 이벤트 대기, wait_for_events() 호출 시 모니터링 중인 파일 디스크립터의 활동 감지.- 타임아웃 매커니즘은 서버 유지보수 및 종료 신호 처리에 필수적.
4. 연결 상태 관리
ConnectionState
구조체는 소켓 파일 디스크립터, HTTP 버퍼, 마지막 활동 시간 등을 저장.- 스레드 안전한 연결 관리를 위해
std::shared_ptr
와std::mutex
사용, 락은 단기 유지.
5. HTTP 요청 파싱의 복잡성
- HTTP 헤더 folding (다중 줄 헤더), Content-Length 누락, 지속적 요청 등 경계 조건 대응 필요.
HttpRequestTask::execute()
에서\r\n\r\n
기준으로 헤더 파싱, HTTP 버퍼링을 통해 부분 요청 처리.
결론
- 고성능 서버 개발 시 이벤트 루프와 I/O 멀티플렉싱(epoll/kqueue) 사용이 필수적.
- ConnectionState와 스레드 안전한 구조를 통해 연결 상태를 효율적으로 관리해야 함.
- HTTP 파싱은 헤더 folding, Content-Length 처리 등 복잡한 경계 조건을 반드시 고려해야 함.
- C10K 문제 해결을 위해 thread-per-connection 모델 대신 이벤트 기반 아키텍처를 도입해야 함.