[Spring Webflux:1편] 비동기 프로그래밍과 리액터

Thread의 역할

Thread는 프로세스 내에서 실행되는 실행 단위이며, 프로그램 코드를 실행한다. 각 Thread는 동시에 실행될 수 있으며, 멀티태스킹 환경에서 다양한 작업을 동시에 처리할 수 있다. Thread는 할당받은 작업을 수행하고, 작업이 완료되면 결과를 반환하거나 다음 작업을 진행한다.

MAC > 활성상태

위 활성상태 정보에서 나타나다시피, 하나의 애플리케이션 내에서 여러 프로세스가 존재할 수 있고, Thread는 프로세스의 메모리와 자원을 공유하면서 독립적인 실행 경로를 가지며, 병렬 작업을 가능한 코드를 실행하는 기본 단위이다.
예를 들어, 하나의 Thread가 사용자의 요청에 대한 응답을 준비하는 동안, 다른 Thread가 데이터베이스에서 정보를 검색하거나 파일 시스템에서 데이터를 읽고 쓰는 작업을 동시에 처리할 수 있다.

Blocking I/O와 Non-Blocking I/O

  • Blocking I/O는 Thread가 데이터를 읽거나 쓸 때 다른 작업을 수행할 수 없고, 작업 완료를 기다려야 하는 모델이다. 이로 인해 Thread 작업을 처리하는 동안 유휴 상태에 빠질 수 있으며, 고부하 상황에서는 Thread 자원이 부족할 수 있다.
  • Non-Blocking I/O는 Thread가 데이터 요청 후 바로 다른 작업을 수행할 수 있으며, 데이터가 준비되면 콜백을 통해 비동기적으로 처리한다. 이는 Thread를 더 효율적으로 사용하며 많은 수의 요청을 동시에 처리할 수 있다.

비동기 처리에서의 Thread 활용 및 데이터 처리 과정

비동기 프로그래밍에서 Thread의 활용은 데이터 요청과 같은 I/O 작업을 효율적으로 처리하는 데 중요한 역할을 한다. Thread는 I/O 작업을 요청한 후, 해당 작업이 완료될 때까지 기다리지 않고 즉시 다른 작업으로 전환할 수 있다. 이렇게 함으로써, 비동기 모델은 스레드가 I/O 작업이 블로킹되는 동안 유휴 상태에 빠지는 것을 방지하고, 시스템 자원을 효율적으로 사용할 수 있도록 한다.

1. 요청 발생: Thread가 데이터베이스 조회나 파일 입출력과 같은 I/O 작업을 요청한다.

2. 요청 비동기 처리: Thread는 요청을 비동기적으로 처리하고, 완료를 기다리지 않고 즉시 다른 작업으로 전환한다. 이는 Thread가 다른 중요한 작업을 계속 진행할 수 있도록 하여 애플리케이션의 응답성을 향상시킨다.

3. 데이터 준비 완료: 요청한 데이터가 준비되면, 이벤트 루프리액터 패턴에 의해 관련 콜백 함수나 처리 로직이 활성화된다. 이 때, 이벤트 루프는 이벤트 큐에 저장된 완료 이벤트를 감지하고 콜백 함수를 실행하여 데이터를 처리하거, 리액터 패턴은 이벤트를 처리할 핸들러 또는 리스너에 도달할 수 있도록 내부적으로 이벤트를 디스패치한다.

4. 데이터 처리: 실행된 콜백 함수는 필요한 데이터 처리를 수행한다. 이 과정에서 데이터 처리 로직은 동일한 Thread 또는 필요에 따라 다른 Thread를 사용할 수도 있다.

리액터 패턴(Reactor Pattern?)

리액터 패턴은 주로 Java의 비동기 서버 개발에서 사용되는 패턴으로, 이벤트의 처리를 위해 단일 또는 여러 이벤트 처리기를 사용하며 Spring Framework의 WebFlux에서 사용된다. 리액터 패턴의 구성 요소는 다음과 같다.

  • 리액터 객체: 이벤트의 도착을 모니터링하고, 해당 이벤트에 맞는 적절한 이벤트 핸들러(또는 콜백)에 이벤트를 전달한다.
  • 이벤트 핸들러: 각 이벤트 유형(예: 읽기, 쓰기, 연결)에 대해 설정된 핸들러가 실제 이벤트 처리 로직을 수행한다.
  • 디스패처: 이벤트를 적절한 이벤트 핸들러에게 전달하는 역할을 한다. 리액터 객체는 디스패처 역할을 수행하면서, 동시에 다수의 이벤트 핸들러를 관리할 수 있다.

⭐️Spring MVC vs Spring Webflux⭐️

Spring MVC
Spring MVC는 서블릿 API를 기반으로 한 전통적인 웹 프레임워크이다. MVC는 각 요청을 개별 Thread에서 동기적으로 처리하며, 주로 Blocking I/O 모델을 사용한다. 서블릿 컨테이너는 요청마다 하나의 Thread를 할당하는 모델을 사용하여, 서버로 들어오는 각 요청을 개별 Thread로 처리한다. 이 Thread들은 요청을 처리하는 동안 다른 요청을 처리할 수 없으며, Thread Pool의 크기는 서블릿 컨테이너의 구성에 따라 수십에서 수백 개까지 설정될 수 있다.
따라서 MVC는 Thread가 특정 작업을 완료할 때까지 기다리는 동안 유휴 상태에 빠질 수 있으므로, 더 많은 Thread와 빈번한 Context Swithing이 필요할 수 있다.

Spring Webflux
Spring Webflux는 비동기 프레임워크로, Non-Blocking I/O 모델을 사용하여 리액티브 프로그래밍을 지원한다. 이 프레임워크는 Netty와 같은 비동기 네트워크 라이브러리를 활용하며, JVM에서 사용 가능한 코어 수에 비례하여 스레드 수를 결정하는 parallel 스케줄러를 통해 Thread를 관리한다. 예를 들어, CPU 코어가 4개인 시스템에서는 기본적으로 4개의 스레드를 사용할 수 있다.
Webflux는 Thread가 Non-Blocking I/O 작업을 요청하면, 작업이 완료될 때까지 기다리지 않고 즉시 반환되어 다른 요청을 처리할 수 있게 한다. 이러한 비동기 모델은 각 Thread가 I/O 작업이 대기 상태일 때 다른 작업으로 쉽게 전환할 수 있도록 하며, 이로 인해 Thread가 더 효율적으로 사용될 수 있다. Webflux는 이벤트 루프 모델을 기반으로 동작하지만, 리액터 패턴을 적극적으로 활용하여 이벤트의 도착을 모니터링하고, 발생한 이벤트를 적절한 이벤트 핸들러에 전달한다.
리액터 패턴은 이벤트의 도착을 감지하고, 이에 반응하는 핸들러들을 동시에 관리할 수 있는 디스패처를 포함한다. 이 패턴은 각 이벤트 유형(예: 읽기, 쓰기, 연결)에 맞게 설정된 핸들러들이 실제 이벤트 처리 로직을 수행하도록 구성된다. 따라서 Webflux는 리액터 패턴을 사용하여 고성능 비동기 데이터 스트림 처리를 가능하게 하고, 시스템의 확장성과 반응성을 크게 향상시킨다.

기준Spring MVCSpring Webflux
프로그래밍 모델동기적, 서블릿 기반비동기적, 리액티브 프로그래밍
I/O 모델Blocking I/ONon-Blocking I/O
Thread 처리요청당 하나의 Thread, 블로킹 처리적은 수의 Thread로 많은 요청 처리, 이벤트 루프 사용
프레임워크 구조서블릿 API에 의존이벤트 루프 모델을 기반으로 동작, 리액터 패턴을 활용한 구조
주요 사용 케이스전통적인 웹 애플리케이션실시간 데이터 처리, 대규모 동시성 요구 애플리케이션
성능과 확장성고부하 시 많은 Thread와 컨텍스트 스위칭 필요높은 동시성과 확장성 제공
Spring MVC vs Spring Webflux

마무리

지금까지 Spring Webflux를 이해하기 위해 프로세스와 스레드, 그리고 Blocking I/O와 Non-Blocking I/O에 대한 개념, 또한 비동기 처리에서의 Thread 활용과 리액터 패턴이 어떻게 작용하는지까지 간단하게 살펴보았다.
Spring MVC와 Spring Webflux는 서로 다른 아키텍처 및 I/O 처리 모델을 바탕으로 각각의 장점을 가지고 있다. Spring MVC는 전통적인 동기적 처리에 최적화되어 있는 반면, Spring Webflux는 비동기적이고 리액티브한 프로그래밍 모델을 통해 더 높은 동시성과 확장성을 제공함으로 각각의 상황에 맞게 적절한 프레임워크를 선택하는 것이 중요하다.
다음 포스팅에서는 Spring Webflux를 활용한 실제 코드 예제를 살펴보고, 리액티브 데이터베이스 연결을 위한 R2DBC에 대해 자세히 탐구할 예정이다. 이를 통해 Webflux가 어떻게 데이터 스트림을 효과적으로 처리하는지, 그리고 JDBC와 다른 데이터베이스 연결과 상호작용하는 방식에 대해서 알아볼 것이다.

[Spring Webflux:1편] 비동기 프로그래밍과 리액터

댓글 남기기

Scroll to top