현대의 높은 네트워크 수요에 적합
- 실시간 처리 수요의 폭발적 증가
- 특히 MMORPG 등의 동접이 많은 게임에서 고성능 서버의 요구는 필연적
- 서비스 품질 향상
- 보다 높은 UX를 위해 서버의 성능과 안정성이 모두 요구됨
- 효율적인 자원 활용
- 보다 효율적인 시스템 자원 활용
- 동시 접속 처리 능력의 제고
- 수십만 단위의 동시 접속을 능히 처리할 수 있을 정도의 고성능 모델
동기 / 비동기
- 동기식 I/O
- I/O 요청을 보낸 후 그 작업이 완료될 때 까지 대기
- 구현은 간단하지만 효율적이지 못함
- 파일을 다 읽을 때 까지 기다리는 것
- 비동기식 I/O
- I/O 요청을 보낸 후 기다리지 않고 바로 다음 작업 수행
- 작업 완료 시에 완료 통지를 받음
- 구현과 관리가 어려우나 효율적으로 동작
- 작업의 완료를 폴링을 통해 주기적으로 확인하는 것
블로킹 / 논블로킹
- 블로킹
- 함수 호출이 완료될 때 까지 대기
- 여러 요청을 처리하기엔 비효율적
- 논블로킹
- 함수가 즉시 반환되지만, 확인은 따로 해야 함
- 폴링 또는 이벤트 기반으로 상태 확인
각각의 비교
- 동기 블로킹
- 파일을 다 읽을 때 까지 기다리는 것
- 동기 논블로킹
- 작업의 완료를 폴링을 통해 주기적으로 확인하는 것
- 비동기 블로킹
await
등을 통해 비동기 작업이 완료될 때 까지 기다리는 것- 비동기 논블로킹
- IOCP 모델이 여기에 해당
- I/O 요청 시 블로킹 되지 않고 다음 코드로 진행
- 해당 작업의 완료를 이벤트나 콜백을 통해 받
I/O Completion Port는 Windows OS에서 제공하는 비동기 I/O 모델 다량의 비동기 I/O 작업을 매우 효과적으로 처리할 수 있기에 MMORPG 등의 서버에 널리 사
IOCP의 개념
- 핸들의 연결
- 소켓 등의 I/O 핸들을 Completion Port에 연결해 중앙에서 관리
- 중앙에서 일관되게 다양한 I/O를 관리할 수 있음
- 완료 통지
- I/O 작업이 완료되면 시스템이 Completion Port에 완료 통지를 걺
- 워커 스레드는 이를 받아 작업을 처리
- Completion Port (완료 포트)
- I/O 작업의 완료 통지를 받는 큐의 역할
- OS와 앱 간의 통신 경로
- Overlapped I/O (오버랩 I/O)
- 비동기 I/O 작업을 수행하기 위한 구조체(
OVERLAPPED
)- 작업이 완료될 때 까지 대기하지 않고
- 다른 작업을 수행할 수 있게 해주는 비동기 I/O 모델
- Worker Thread (워커 스레드)
- 완료 통지를 받아 실제 작업을 처리하는 스레드
- 스레드 풀을 구성해 효율성을 끌어올린다
출처: https://developstudy.tistory.com/43
IOCP의 동작 흐름은 다음의 과정으로 간략히 표현할 수 있다
- Completion Port 생성
CreateIoCompletionPort
를 통해 Completion Port 생성
- 소켓 또는 핸들을 Completion Port에 연결
- 여기서 연결된 소켓에 완료 통지가 전달됨
- 워커 스레드 생성
- 각 스레드에선
GetQueuedCompletionStatus
함수를 통해 I/O 완료 통지를 대기
- 비동기 I/O 작업 요청
WSARecv
또는WSASend
를 통한 비동기 I/O 작업 요청- 이때,
OVERLAPPED
를 사용하며, 함수는 즉시 리턴
- I/O 작업 진행
- OS가 백그라운드에서 작업을 진행 후 완료 포트에 통지
- 통지 수신
- 워커 스레드는
GetQueuedCompletionStatus
를 통해 대기하고 있다가 통지 수신
- 워커 스레드 작업 시작
- 받은 정보를 바탕으로 워커 스레드는 작업을 시작
- 종료 또는 반복
- 필요하다면 추가적 I/O 작업을 요청해 위 과정을 반복한다
- 클라이언트 연결이 종료되면 소켓을 닫고 완료한다
이는 모든 비동기 I/O 시스템이 공유하는 고려사항이기도 함
- 스레드 관리
- 최적의 스레드 수 결정
- 시스템 성능을 고려한 최적의 스레드 수 결정
- 스레드 풀 관리
- 부하에 따라 스레드 풀을 유동적으로 조절
- 메모리 관리
- 버퍼 풀링
- 메모리 할당과 해제는 꽤 비용이 높음
- 버퍼를 미리 메모리에 할당해 두고 재활용
- 버퍼 풀을 준비할 수도 있음
- C++을 통해 구현되기 때문에 메모리 관리에 특히 주의해야 함
- 동기화 관리
- 데드락을 방지하기 위한
Lock-Free
알고리즘 사용- 락의 획득 순서 관리와 타임아웃 등을 통한 교착 상태 방지