Reactor Netty는 비동기 논블로킹 이벤트 루프 기반의 네트워크 프레임워크로, 하나의 스레드에서 I/O 작업을 논블로킹으로 처리할 수 있다. 어떻게 하나의 스레드로 수많은 I/O 작업을 논블로킹으로 처리할 수 있는걸까?
Netty의 동작 방식과 Tomcat NIO와의 차이점을 비교하면서 동작원리를 살펴보자.
Reactor Netty는 Netty 기반의 논블로킹 I/O 서버이며, 이벤트 루프(Event Loop) 모델을 활용하여 싱글 스레드에서도 다중 연결을 처리할 수 있도록 설계되었다.
하나의 스레드가 여러 네트워크 요청을 처리할 수 있는 이유는 이벤트 루프 + 논블로킹 I/O 모델 때문이다.
1. 클라이언트 요청 도착
+------------------------+
| Client Request |
| (TCP Connection) |
+------------------------+
|
v
2. Tomcat Acceptor Thread에서 요청 감지
+-----------------------------+
| Tomcat Acceptor Thread |
| - Accepts TCP Connection |
| - Assigns Worker Thread |
+-----------------------------+
|
v
3. NIO Selector가 소켓을 감시
+---------------------------+
| Java NIO Selector |
| - Uses select()/poll() |
+---------------------------+
|
v
4. 데이터가 준비되면 Worker Thread에서 처리
+------------------------+
| Worker Thread |
| - Reads Request Data |
+------------------------+
|
v
5. 서블릿에서 비즈니스 로직 처리
+------------------------+
| Servlet Container |
| - Executes Business Logic |
+------------------------+
|
v
6. Worker Thread에서 응답 생성 후 전송
+------------------------+
| Worker Thread |
| - Sends Response |
+------------------------+
|
v
7. 클라이언트 응답 수신
+------------------------+
| Client Receives Data |
+------------------------+
1. 클라이언트 요청 도착
+------------------------+
| Client Request |
| (TCP Connection) |
+------------------------+
|
v
2. Reactor Netty의 Event Loop에서 요청 감지 (Selector 등록)
+------------------------+
| Event Loop (I/O Thread)|
| - Registers socket FD |
| - Non-blocking I/O |
| - Uses epoll/kqueue |
+------------------------+
|
v
3. OS 커널에서 epoll/kqueue에 소켓 등록 (I/O 대기)
+--------------------------+
| OS Kernel (epoll/kqueue) |
| - Watches socket events |
| - No CPU block |
+--------------------------+
|
v
4. 데이터가 준비되면 커널에서 이벤트 발생
+--------------------------+
| OS Kernel Event Trigger |
| - I/O Ready |
| - Notifies Event Loop |
+--------------------------+
|
v
5. 이벤트 루프가 큐에 작업 추가 (Task Queue)
+--------------------------+
| Event Loop Task Queue |
| - Stores I/O Events |
| - Processes Tasks Async |
+--------------------------+
|
v
6. 이벤트 루프가 Task Queue에서 이벤트를 꺼내어 실행
+----------------------------+
| Reactor Netty Pipeline |
| - Decodes Request |
| - Business Logic Execution |
| - Encodes Response |
+----------------------------+
|
v
7. OS를 통해 응답 전송 (비동기)
+--------------------------+
| OS Kernel (epoll/kqueue) |
| - Send Response |
+--------------------------+
|
v
8. 클라이언트 응답 수신
+------------------------+
| Client Receives Data |
+------------------------+
Reactor Netty가 사용하는 논블로킹 I/O 모델은 OS 커널의 I/O 멀티플렉싱 기법(epoll, kqueue, select)을 활용한다.
이 방식을 통해 I/O 작업이 준비될 때만 이벤트를 발생시키며, CPU가 대기하는 것을 방지한다.
OS에서 네트워크 I/O 이벤트를 감지하는 방법에는 여러 가지가 있으며, 대표적으로 select, poll, epoll(Linux), kqueue(BSD/macOS) 등이 있다.
기법 | 지원 OS | 파일 디스크립터(FD) 제한 | 작동 방식 | 성능 |
---|---|---|---|---|
select | 모든 OS | FD_SETSIZE 제한 (1024) | 모든 FD를 검사 (O(N)) | 느림 (O(N)) |
poll | 모든 OS | 제한 없음 | 전체 FD를 순차 검색 (O(N)) | 느림 (O(N)) |
epoll | Linux | 제한 없음 | 이벤트 기반 감지 (O(1)) | 빠름 (O(1)) |
kqueue | BSD/macOS | 제한 없음 | 이벤트 기반 감지 (O(1)) | 빠름 (O(1)) |
epoll_create()
를 호출하여 epoll 인스턴스를 생성epoll_ctl()
을 통해 감시할 FD를 등록 (O(1)) -> FD를 red-black tree에 저장하여 추가/삭제 연산을 O(log N)으로 처리epoll_wait()
을 호출하여 이벤트가 발생한 FD만 반환 (O(1)) -> 이벤트가 발생한 FD는 ready list에 따로 저장epoll은 파일 디스크립터(FD)를 red-black tree에 저장하고, 이벤트가 발생하면 ready list에서 즉시 가져올 수 있도록 설계되어 있다.
kqueue()
를 호출하여 kqueue 인스턴스를 생성kevent()
를 통해 감시할 FD를 등록 (O(1))kevent()
를 호출하여 이벤트가 발생한 FD만 반환 (O(1))