1. Spring Webflux 비동기 처리 흐름의 이해 - Netty 편
2. Spring Webflux 비동기 처리 흐름의 이해 - Reactor 편
이 글은 WebFlux 기반 웹 서버의 비동기 동작 흐름을 중심으로 설명합니다.
요청 처리의 Thread 흐름을 중심으로 작성했으며, 해당 관점에서 벗어난 개념은 자세히 설명하지 않습니다.
만약 자바 비동기 프로그래밍에 대한 사전 지식이 없다면 해당 시리즈를 먼저 읽으시면 이해에 도움이 됩니다.
1. Webflux 구성 (Netty, Reactor)
Webflux는 Reactor Netty를 기반으로 동작하는 비동기 웹 프레임워크 입니다.
Reactor Netty는 크게 Netty와 Reactor로 구성되어 있습니다.
Netty
Netty는 비동기 이벤트 루프 기반 네트워크 프레임워크 입니다. 비동기 I/O 작업을 처리할 수 있도록 설계되었으며 주로 HTTP 요청을 비동기적으로 읽고 처리합니다.
Reactor
Reactor는 비동기 애플리케이션 프로그래밍을 고수준 API로 할 수 있도록 도와주는 라이브러리 입니다.
Reactor Netty와 Spring Webflux
Netty와 Reactor는 서로 다른 모듈이며, Reactor Netty가 이 둘을 연결해주는 어댑터 혹은 브릿지와 같은 역할을 해줍니다.
그리고 Spring Webflux는 Reactor Netty를 기반으로 동작하는 웹 프레임워크 입니다.
개념인 내용은 위와 같지만 위 내용으로는 이들이 어떻게 동작하는건지 이해하기 어렵습니다.
이 글에서는 Netty의 동작과 Reactor의 개념, 두 기술이 어떻게 연결되어 Webflux가 동작하는지 알아보겠습니다.
2. Netty의 동작
Netty가 HTTP 요청을 비동기로 처리할 수 있도록 하는 이벤트 루프 모델을 기반으로 합니다.
Event Loop
전통적인 Thread Per Request 모델
전통적인 Thread Per Request 방식과 비교하면 클라이언트 요청 하나당 Thread 하나에서 처리하며, I/O 가 발생하면 해당 Thread가 Blocking되고 동기적으로 응답을 기다립니다. 따라서 CPU 관점에서는 Blocking된 Thread 대신 작업을 수행해야하는 Thread를 호출하며 Context Switching을 하면서 오버헤드가 생기게 됩니다.
Event Loop 모델
Event Loop는 비동기 논블로킹 프로그램 모델을 구현하는 패턴입니다. Event Loop 모델은 네트워크 I/O를 이벤트로 감지하여 이벤트가 발생할 때만 작업을 수행합니다. 따라서 클라이언트와의 통신 간의 발생하는 네트워크 I/O가 발생하면 이를 Thread는 이를 대기하지 않고 다른 작업을 먼저 처리합니다.
Netty에서는 멀티쓰레드 기반의 이벤트 루프 모델로 구현되었으며 기존 방식보다 훨씬 적은 쓰레드로 더 많은 요청을 수행할 수 있게 설계되었습니다.
Netty는 다음과 같은 쓰레드를 이용해 요청 수신과 처리를 효율적으로 수행합니다.
- Boss Group
- Worker Group
Boss Group
Boss Group은 클라이언트의 TCP 연결 요청을 수락하는 역할을 담당하며, 일반적으로 하나의 스레드가 이를 처리합니다.
Worker Group
Worker Group에서는 Boss Group에서 수락한 요청의 내용을 읽고 처리하여 응답하는 역할을 합니다.
Webflux에서 기본적으로 (core 개수 * 2) 개의 Thread를 할당합니다.
또한 Netty의 이벤트 루프는 Java nio 패키지의 Channel, Selector라는 주요 구성요소를 통해 동작합니다.
Channel
Channel은 OS 커널의 I/O 서비스와 상호작용하며, 논블로킹 방식으로 동작하기 때문에 비동기적인 네트워크 읽기/쓰기 작업이 가능합니다.
Boss Group Thread는 하나의 ServerSocketChannel을 관리하며, 클라이언트의 연결 요청을 감지하고 수락하는 역할을 합니다. 연결이 수락되면, 클라이언트와 연결된 SocketChannel을 생성하여 적절한 Event Loop에 전달합니다.
또한, Boss Group Thread는 각 Event Loop에 등록된 Channel의 개수를 확인하면서 로드 밸런싱을 수행하여 작업을 분산합니다. 즉, 하나의 Event Loop에는 여러 개의 Channel이 등록될 수 있습니다.
Selector
Selector는 네트워크 이벤트가 발생했을 때 감지하고 작업 큐에 이벤트를 등록하는 역할을 합니다.
Java NIO의 클래스로, OS 커널의 이벤트 통지 메커니즘(Linux의 epoll 등)을 활용합니다.
Event Loop Group마다 하나의 Selector를 가지고 있으며 이를 통해 이벤트를 감지합니다.
이벤트는 네가지 종류가 있습니다.
- ACCEPT (OP_ACCEPT)
- CONNET (OP_CONNET)
- READ (OP_READ)
- WRITE (OP_WRITE)
ACCEPT
클라이언트가 서버로 요청을 할 때 발생하며 Boss Thread가 처리합니다.
ACCEPT 이벤트가 발생하면 Boss Thread의 Selector가 이를 감지합니다. Boss Thread에서는 요청에 대한 Socket Channel을 생성하고 Woreker Thread에 전달하고 Worker Thread는 해당 채널을 Selector에 등록합니다.
CONNECT
클라이언트와의 연결이 성공적으로 수립됐을 때 발생하는 이벤트입니다.
CONNECT 이벤트가 발생하면 Channel 이 클라이언트와 통신할 수 있도록 초기화 작업을 합니다.
READ
클라이언트의 요청을 읽을 준비가 됐을 때 발생하는 이벤트입니다.
READ 이벤트가 발생하면 Http 요청을 해석해서 적절한 컨트롤러에 전달하고 요청을 수행하며 응답을 Channel에 Write 합니다. 이 작업은 Worker Thread에서 수행되기도 하지만, 애플리케이션 로직에 비동기 작업이 있었다면 별도의 Thread에서 수행될 수도 있습니다. 이 내용은 Reactor 목차 및 전체 흐름 예시에서 다시 다룹니다.
만약 소켓 버퍼가 가득 찬 경우에는 응답을 보류하고 Selector의 WRITE 이벤트가 발생했을 때 처리합니다.
WRITE
WRITE 이벤트는 다음과 같은 조건을 만족할 때 발생합니다.
- READ 단계에서 애플리케이션 로직을 모두 수행했으나 소켓 버퍼가 가득 차서 보류된 응답이 있음
- 소켓 버퍼가 가득 찼다가 다시 공간이 생김
이 경우, 보류된 응답에 대해 애플리케이션 로직과 관계없이 Worker Thread가 소켓 버퍼에 응답 쓰기 작업을 진행합니다.
참고
Boss Group Thread는 요청 수락 외에 다른 작업을 하지 않기 때문에 Selector에서 이벤트가 감지되지 않으면 Blocking 상태로 대기합니다.
Worker Group Thread는 여러 채널의 이벤트를 감지해야 하고, 작업 큐 및 다른 스케쥴에 등록된 작업들도 처리해야 하기 때문에 Selector로부터 일정시간 이벤트가 감지되지 않으면 Time out 되고 다른 작업이 있는지 확인하고 수행합니다.

이 그림은 위의 설명을 표현합니다. 그림과 함께 개념을 요약하면 다음과 같습니다.
- Boss Thread는 Selector를 통해 ServerSocketChannel에 요청이 있는지 감지합니다.
- 클라이언트 요청을 감지하면 이를 수락하고 SocketChannel을 생성합니다.
- 생성한 SocketChannel을 균등하게 Worker Thread에게 전달합니다.
- Worker Thread는 전달받은 Channel을 Selector에 등록합니다.
- Worker Thread는 Selector로부터 Channel의 이벤트를 감지하고 요청을 처리하며, 처리 결과를 결과를 클라이언트에게 응답합니다.
여기까지 Netty의 전반적인 동작 흐름을 알아봤습니다.
다음 편에서는 Reactor의 동작을 다룹니다.
참고자료
'Spring' 카테고리의 다른 글
| Spring Webflux 비동기 처리 흐름의 이해 - 2. Reactor 편 (0) | 2025.04.01 |
|---|