Spring vs Spring Webflux

박소미·2024년 7월 22일

Spring Webflux란?
Spring webflux는 리액티브 웹 애플리케이션 구현을 위해 Spring 5.0부터 지원되는 Reactive Web Framework이다.

Spring MVC

Spring MVC는 서블릿 기반의 Blocking I/O 방식을 사용한다.

📌Blocking I/O VS Non-Blocking I/O
⇒ 다른 주체가 작업할 때 물리적으로 자신의 제어권이 있는지가 기준이다. (물리적인 실행 특성)

✔️ Blocking I/O

  • 자신의 작업을 진행하다가 다른 주체의 작업이 시작되면, 다른 작업이 끝날 때까지 기다렸다가 자신의 작업을 시작하는 방식이다.
    물리적으로 I/O 작업이 끝날 때까지 다른 작업을 하지 않고 기다리는 것을 의미한다.

  • ex) 파일을 읽거나 네트워크 요청을 처리할 때, I/O 작업이 완료될 때까지 해당 스레드는 다른 작업을 할 수 없다.

    ✔️ Non-Blocking I/O
  • 다른 주체의 작업에 관계없이 자신의 작업을 수행할 수 있는 방식이다.
    I/O 작업이 진행되는 동안에도 물리적으로 대기하지 않고, 다른 작업을 수행할 수 있다.

  • ex) 네트워크 요청을 보낸 후, 응답이 올 때까지 기다리지 않고 다른 요청을 처리하거나 다른 작업을 계속 수행하는 것.

Spring MVC의 특징:

  1. 요청당 하나의 스레드 사용:
  • HTTP 요청이 들어오면, 그 요청을 처리하기 위해 하나의 스레드가 할당된다.
  • 이 스레드는 작업이 완료될 때까지 차단된다(blocking).
  1. 스레드 풀:
  • Spring MVC는 기본적으로 스레드 풀(thread pool)을 사용하여 요청을 처리한다.

  • 기본적으로 약 200개의 스레드를 가지고 있다. 이 스레드가 모두 사용 중일 때 추가 요청이 들어오면 대기 상태가 된다.

  1. 한계:
  • 트래픽 증가: 많은 사용자가 동시에 접근할 경우, 더 많은 스레드가 필요하게 된다. 만약 만 명이 동시에 접속하면 200개의 스레드로는 처리하기 어렵다.

  • 스레드 스위칭 비용: 스레드를 많이 사용할수록 스레드 간의 전환 비용이 증가하게 된다. 이는 성능 저하를 초래한다.

Spring WebFlux

Spring WebFlux는 대용량 트래픽을 효과적으로 처리하기 위해 비동기/Non-Blocking I/O 방식을 사용한다.

Spring WebFlux의 특징:
1. 비동기/Non-Blocking I/O:

  • 요청을 처리하는 동안 스레드가 차단되지 않는다. 즉, 요청 처리가 완료될 때까지 스레드가 대기 상태에 머물지 않는다.

  • 이는 이벤트 루프(event loop) 모델을 사용하여 비동기적으로 작업을 처리한다.

  1. 적은 스레드로 많은 요청 처리:
  • 비동기 방식은 적은 수의 스레드로도 많은 요청을 처리할 수 있게 한다. 이는 스레드 스위칭 비용을 줄이고, 시스템 자원의 효율성을 높일 수 있다.
  1. 리액티브 프로그래밍:
  • Spring WebFlux는 리액티브 프로그래밍 모델을 사용한다. 이는 데이터를 스트림 형태로 처리하고, 비동기적으로 이벤트를 처리하는 방식을 말한다.
    Reactor 라이브러리를 사용하여 이러한 비동기, 논블로킹 방식의 I/O를 구현한다.
  1. 장점:
  • 높은 확장성: 대용량 트래픽을 안정적으로 처리할 수 있다.

  • 효율성: 리소스 사용을 최적화하여 더 많은 요청을 처리할 수 있다.

  1. 주요 구성 요소:
  • Mono: 0 또는 1개의 요소를 비동기적으로 반환하는 객체.

  • Flux: 0부터 N개의 요소를 비동기적으로 반환하는 객체.
    이들을 사용하여, 개발자는 비동기적으로 데이터를 처리하고, 트래픽이 많은 상황에서도 효율적으로 시스템을 운영할 수 있다.

📌 Spring WebFluxSpring MVC의 한계를 극복하기 위해 등장한 솔루션으로, 비동기/논블로킹 I/O 방식을 통해 대용량 트래픽을 안정적으로 처리할 수 있다. 이를 통해 스레드 사용량을 줄이고, 시스템 성능을 향상시킬 수 있다.

MVC와 WebFlux의 차이를 예시를 통해 이해하기

🎯시나리오
클라이언트가 백엔드 서버로 요청을 보낸다. 백엔드 서버는 외부 서버로 요청을 보내고, 외부 서버의 동작은 5초가 걸린다고 가정한다. 클라이언트의 요청이 총 5번 들어왔을 때의 상황을 비교해볼 것이다.

1. MVC의 경우
Spring MVC는 서블릿 기반의 동기식 처리를 수행한다. 외부 서버로 요청을 보낼 때 동기 처리 방식인 RestTemplate을 사용한다.

  • 동작 과정:

      1. 클라이언트의 요청 1이 백엔드 서버에 도착한다.
      1. 백엔드 서버는 외부 서버로 요청을 보낸다.
      1. 외부 서버는 5초 후 응답을 보낸다.
      1. 백엔드 서버는 응답을 클라이언트에게 반환한다.

이 과정을 5번 반복합니다. 즉, 각 요청이 완료될 때까지 백엔드 서버의 스레드는 블로킹된다.

  • 시간 계산:

5번 요청 * 5초(외부 서버 응답 시간) = 총 25초
각 요청이 순차적으로 처리되기 때문에, 총 25초가 걸린다.

코드 예시

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;

@RestController
public class MvcController {

    // GET 요청을 받을 때 "/mvc" 엔드포인트를 처리하는 메소드
    @GetMapping("/mvc")
    public String handleRequest() {
        // RestTemplate 객체를 생성. RestTemplate는 동기 방식으로 외부 서버와 통신할 때 사용됨.
        RestTemplate restTemplate = new RestTemplate();

        // 외부 서버로 GET 요청을 보내고 응답을 String 타입으로 받아온다.
        // "http://external-service.com"은 요청을 보낼 외부 서버의 URL.
        String response = restTemplate.getForObject("http://external-service.com", String.class);

        // 외부 서버의 응답을 클라이언트에게 반환합니다.
        return response;
    }
}

2. WebFlux의 경우
Spring WebFlux는 비동기 논블로킹 방식의 리액티브 프로그래밍을 지원한다. 외부 서버로 요청을 보낼 때 비동기 방식인 WebClient를 사용합니다.

  • 동작 과정:

      1. 클라이언트의 요청 1이 백엔드 서버에 도착한다.
      1. 백엔드 서버는 외부 서버로 비동기 요청을 보낸다.
      1. 외부 서버의 응답을 기다리지 않고, 백엔드 서버는 다른 요청을 처리한다.
      1. 외부 서버가 응답을 보내면, 백엔드 서버는 해당 응답을 클라이언트에게 반환한다.

이 과정이 비동기적으로 수행되기 때문에, 5번의 요청을 동시에 처리할 수 있다.

  • 시간 계산:

외부 서버 응답 시간 5초 + 비동기 처리 = 총 5초
블로킹 없이 모든 요청이 동시에 처리되기 때문에, 총 5초가 걸린다.

코드예시

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.reactive.function.client.WebClient;
import reactor.core.publisher.Mono;

@RestController
public class WebFluxController {

    // WebClient 객체를 생성. WebClient는 비동기식으로 외부 서버와 통신할 때 사용된다.
    private final WebClient webClient = WebClient.create("http://external-service.com");

    // GET 요청을 받을 때 "/webflux" 엔드포인트를 처리하는 메소드
    @GetMapping("/webflux")
    public Mono<String> handleRequest() {
        // WebClient를 사용하여 외부 서버로 GET 요청을 보낸다.
        // retrieve() 메소드는 응답 본문을 읽어온다.
        // bodyToMono(String.class)는 응답 본문을 Mono<String> 타입으로 변환한다.
        return webClient.get()
                        .retrieve()
                        .bodyToMono(String.class);
    }
}

0개의 댓글