Indy 서버는 스레드를 사용해 클라이언트 연결을 처리해요. 새 클라이언트가 서버에 연결할 때마다 새 스레드가 생성되어 해당 연결을 처리해요. 100개의 연결이 있으면 100개의 스레드가 만들어져요. 또한 Indy는 블로킹 소켓을 사용해요. 즉, 읽기나 쓰기 시 완료될 때까지 함수가 반환되지 않아요.
이 모델은 코드가 순차적으로 처리되므로 코딩이 쉽다는 장점이 있어요. 하지만 연결 수가 증가하면 스레드 컨텍스트 전환으로 인해 성능이 점점 나빠지는 단점이 있어요. 컨텍스트 전환은 스레드 상태를 저장해 나중에 실행을 재개할 수 있도록 복원하는 프로세스예요. 스레드 간의 빠른 컨텍스트 전환은 CPU 사용 측면에서 비용이 많이 들어요. 서버에 1000개의 연결을 만들면 데이터 교환이 없어도 CPU가 작동하는 것을 볼 수 있는데, 이는 스레드 컨텍스트 전환 때문이에요.
Indy 모델의 대안
연결마다 1개의 스레드를 사용하는 대신, 연결 처리에 스레드 풀을 사용하고 논블로킹 소켓을 사용하는 IOCP(Windows용) 또는 EPOLL(Linux용) 같은 대안이 있어요. 이 모델은 동시 연결 수가 많을 때 훨씬 효율적이고 기본 Indy 스레드 모델보다 훨씬 잘 확장돼요.
IOCP (Windows)
I/O 완료 포트는 멀티프로세서 시스템에서 여러 비동기 I/O 요청을 처리하기 위한 효율적인 스레딩 모델을 제공해요. 프로세스가 I/O 완료 포트를 만들면 시스템은 이러한 요청을 처리하는 것만이 목적인 스레드를 위한 관련 큐 객체를 만들어요. I/O 요청을 받을 때 스레드를 만드는 것보다 미리 할당된 스레드 풀과 함께 I/O 완료 포트를 사용하면 많은 동시 비동기 I/O 요청을 더 빠르고 효율적으로 처리할 수 있어요.
IOCP 모델은 논블로킹 소켓을 사용하고, Select 대신 AcceptEx 함수와 스레드 풀을 사용해 클라이언트 연결을 처리해요.
sgcWebSockets Indy 서버에서 IOCP를 활성화하려면 아래 코드를 확인하세요
oServer := TsgcWebSocketHTTPServer.Create(nil); oServer.NotifyEvents := neNOSync; oServer.IOHandlerOptions.IOHandlerType := iohIOCP; oServer.Active := True;
새 WebSocket + HTTP 서버가 생성되고 스레드 풀이 연결을 처리해요. 사용되는 스레드 수는 서버가 실행 중인 CPU 수에 따라 달라져요.
EPOLL (Linux)
Epoll은 Linux 커널 2.5.44 버전에서 처음 도입된 확장 가능한 I/O 이벤트 알림 메커니즘을 위한 Linux 커널 시스템 호출이에요. 여러 파일 디스크립터를 모니터링해 어떤 것에서 I/O가 가능한지 확인하는 기능이에요. 감시하는 파일 디스크립터 수가 많은 더 까다로운 애플리케이션에서 더 나은 성능을 위해 오래된 POSIX select 및 poll 시스템 호출을 대체하도록 설계됐어요.
100,000개의 모니터링 작업에 대한 성능을 비교하는 다음 표를 확인하세요:
| 작업 수 | poll | select | epoll |
| 10 | 0.61 | 0.73 | 0.41 |
| 100 | 2.9 | 3.0 | 0.42 |
| 1000 | 35 | 35 | 0.53 |
| 10000 | 990 | 930 | 0.66 |
파일 디스크립터가 10개 이상인 경우 epoll을 사용하면 훨씬 빠르게 동작해요.
EPOLL 모델은 논블로킹 소켓을 사용하고, Select 대신 Accept 비동기 함수와 스레드 풀을 사용해 클라이언트 연결을 처리해요.
sgcWebSockets Indy 서버에서 EPOLL을 활성화하려면 아래 코드를 확인하세요
oServer := TsgcWebSocketHTTPServer.Create(nil); oServer.NotifyEvents := neNOSync; oServer.IOHandlerOptions.IOHandlerType := iohEPOLL; oServer.Active := True;
