본문으로 건너뛰기

다중 접속 서버의 구현 방식

1. 블로킹 TCP 서버의 한계

1.1 문제점

  • 다수의 클라이언트가 연결하는 경우에는 문제가 발생합니다.
  • 처음 연결한 클라이언트가 연결을 종료하기 전까지는 다른 클라이언트의 연결은 listen 큐에 들어가 대기해야 합니다.
  • 따라서 다수의 요청을 처리할 수 없다는 문제가 있습니다.

1.2 해결 방안 개요

다중 접속 서버는 다수의 클라이언트가 동시에 접속할 수 있는 서버입니다. 구현 방식은 크게 다음과 같습니다:

  • 멀티 프로세싱: fork() 함수를 사용해 자식 프로세스를 생성하는 방식
  • 멀티 스레딩: 스레드를 사용해 다수의 클라이언트 요청을 처리하는 방식
  • I/O 멀티플렉싱: select() 함수를 사용해 다수의 소켓을 모니터링하고, 이벤트가 발생한 소켓을 처리하는 방식

2. 멀티 프로세싱 기반 다중 접속 서버

2.1 동작 방식

  1. 연결 수락 과정
    • 부모 프로세스는 리스닝 소켓으로 accept 함수를 호출해서 연결 요청을 수락합니다.
    • 이때 얻는 소켓의 파일 디스크립터(클라이언트와 연결된 연결 소켓)를 자식 프로세스를 생성해 넘겨줍니다.
  2. 서비스 제공
    • 자식 프로세스는 전달받은 파일 디스크립터를 바탕으로 서비스를 제공합니다.
    • 핵심은 하나의 연결이 생성될 때마다 프로세스를 생성해서 해당 클라이언트에 대해 서비스를 제공하는 방식입니다.

2.2 장점

  • 프로그램 흐름이 단순하기 때문에 이해하기 쉽습니다.
  • 안정적인 동작이 가능합니다. 운영 체제에서 프로세스는 서로 독립된 실행 객체로 존재합니다.
  • 서로 독립된 메모리 공간을 갖고 각 프로세스는 서로 영향을 미치지 않기 때문에 독립적으로 수행이 가능합니다.

2.3 단점

  • 프로세스 복사가 필요하기 때문에 리소스를 많이 사용합니다.
  • 병렬로 처리해야 하는 수만큼 프로세스를 생성해야 합니다.
  • 서로 다른 독립적인 메모리 공간을 갖기 때문에 프로세스간 정보 교환이 어렵습니다.

3. 멀티 스레딩 기반 다중 접속 서버

3.1 개요

Blocking 소켓에서 다중 클라이언트의 접속 처리를 하지 못하는 문제점을 해결하기 위해서 등장한 모델이 연결된 클라이언트별로 각각 스레드를 할당하는 방식입니다.

3.2 동작 방식

  1. 연결 수락
    • 메인 스레드는 리스닝 소켓으로 accept 함수를 호출해서 연결 요청을 수락합니다.
    • 이때 얻는 소켓의 파일 디스크립터(클라이언트와 연결된 연결 소켓)를 별도 워커 스레드를 생성해 넘겨줍니다.
  2. 서비스 제공
    • 워커 스레드는 전달받은 파일 디스크립터를 바탕으로 서비스를 제공합니다.
    • 핵심은 연결이 하나 생성될 때마다 프로세스가 아닌 스레드를 생성해서 해당 클라이언트에게 서비스를 제공하는 것입니다.
  3. 스레드 풀 활용
    • 매번 스레드를 생성하는 것이 아니라 미리 생성해 놓은 스레드 풀에서 스레드를 가져와 사용할 수도 있습니다.

3.3 장점

  • 프로세스 복사 비용보다 스레드 생성 비용이 적습니다.
  • 서로 공유하는 메모리가 있기 때문에 스레드간 정보 교환이 쉽습니다.

3.4 단점

  • 하나의 프로세스 내에 다수의 스레드가 존재하기 때문에 하나의 스레드에서 발생한 문제가 프로세스 전체에 영향을 미쳐 나머지 다수의 스레드에 영향을 끼칠 수 있습니다.
  • 멀티프로세싱 방식보다는 비용이 적게 들지만 스레드 관리에 여전히 많은 리소스가 필요합니다.
  • 일정 크기의 스레드를 생성해 풀로 관리하며 운영할 수 있지만 요청마다 스레드를 무한정 생성할 수 없기 때문에 많은 수의 요청을 동시에 처리할 수 없습니다(C10k problem을 해결하지 못합니다).
  • 커넥션별로 스레드를 생성하기때문에 스레드가 기하급수적으로 많아질 수도 있습니다. 이때 스레드간의 컨텍스트 스위칭 하는 과정에서 CPU 시간과 리소스를 소모합니다.
  • 다중 연결을 처리할 수 있는 가장 기본적인 서버 구조이나, 요청이 증가하면 선형적으로 서버의 부하가 걸리는 구조라는 치명적인 문제가 있습니다.

4. 멀티플렉싱 기반 다중 접속 서버

4.1 개요

  • 커널(kernel)에서는 하나의 스레드가 여러 개의 소켓(파일)을 핸들링할 수 있는 select, poll, epoll, io_uring과 같은 시스템 콜(system call)을 제공하고 있습니다.

4.2 구현 방식

  • Java에서는 NIO(New I/O) 패키지를 통해 멀티플렉싱 기반의 다중 접속 서버를 구현할 수 있습니다.
  • 하나의 스레드로 여러 연결을 동시에 처리할 수 있어 자원 효율성이 높습니다.

4.3 장점

  • 단일 스레드로 다중 연결 처리 가능
  • C10k problem 해결
  • 컨텍스트 스위칭 최소화
  • 시스템 자원 효율적 사용

4.4 단점

  • 구현 복잡도가 높음
  • CPU 집약적 작업에 취약
  • 비동기 프로그래밍 모델 적응 필요