spring webflux 개념

Q·2024년 4월 28일

Spring WebFlux

목록 보기
1/2

용어

동기(Sync)

  • 동기는 말 그대로 동시에 일어날 수 있다는 뜻인데, 호출과 응답이 동시에 이루어지는 것을 말한다.
  • 다시 말해, 함수를 호출한 곳에서 바로 응답을 받는다.

비동기(Async)

  • 비동기는 호출과 응답이 동시에 이루어지지 않는 것을 의미한다.
  • 호출 시점과 처리결과에 대한 응답시점이 같지 않아서 함수를 호출했을 때 그에 대한 처리결과를 추후에 처리가 완료된 불특정 시점에 전달받는다.

블로킹(Blocking)

  • 함수를 call 했을 대 응답을 받기 위해 멈춰있는 상태를 의미한다.
  • 기본적으로 코딩테스트를 진행할 때 메인 메소드 내부에서 method() 함수를 호출했을 때 해당 함수가 수행된다. 이때 해당 함수의 로직이 수행되고 함수의 return이 있고나서 함수가 종료된다. 이후 메인 메소드에서 해당 함수를 호출한 다음 줄의 코드를 실행할 수 있다.

논블로킹(Non-Blocking)

  • 블로킹 방식과 다르게 메인 메소드 내부에서 method() 함수를 호출하더라도 그 결과를 리턴받지 않아도 바로 다음줄을 실행할 수 있는 상태를 의미한다.
  • 즉, method() 함수에서 리턴되지 않고 아직 수행 중이더라도 메인 메소드는 다음 줄을 수행한다.

Spring MVC vs Spring WebFlux

Spring MVC

  • Spring MVC는 Servlet 기반으로 만들어졌고, sync + blocking 방식으로 동작하고 결국 하나의 처리를 할 때 Response를 기다리며 thread를 지연시키는 부분이 있다.

  • 기본적으로 사용자 요청이 Spring 서버에 들어오게 된다면, ThreadPool에서 존재하는 스레드를 하나 할당하고 해당 스레드는 그 요청을 처리해야한다. 만약 사용자 요청이 디스크에 오래 걸리는 작업을 요청할 경우 해당 스레드는 디스크 쓰기 작업이 완료될 때까지 기다리는 상황이 생기는데 이를 Blocking이라고 하는 것이다.

  • 따라서 Spring MVC 같은 경우 요청이 들어오면 그 요청을 Queue에 쌓고 순서에 따라서 Thread를 하나 점유해 요청을 처리한다. 동시 다발적으로 스레드 수를 초과하는 요청이 발생한다면 계속해서 요청이 큐에 대기하게 되는 Thread Poll Hell 현상이 발생할 수 있다. 이를 해결하기 위해 시스템의 트래픽을 측정해서 thread pool size를 잘 조정해야한다. 하지만 사이즈를 조정을 하더라도 사용자의 요청을 대량으로 받아내는데는 한계가 있을 것이다. 이를 해결하는 것이 Webflux인 것이다.

Webflux

  • Webflux는 요청을 처리하는 방식이 Event-Driven 방식이고 async + nonblocking 방식이다.

  • 작은 수의 스레드(일반적으로 CPU 코어 수에 비례)로 많은 수의 동시 요청을 처리한다.

  • 간단하게 설명하자면, 이벤트 루프가 돌아서 요청이 발생할 경우 맞는 핸들러에게 처리를 위임하고 처리가 완료되면 callback 메소드 등을 통해 응답을 반환한다.

  • 요청이 처리될 때까지 기다리지 않기때문에 사용자의 요청을 대량으로 받아낼 수 있다는 장점이 있다.

  • 즉, 서버 프로그램이 효율적으로 동작해서, cpu, thread, memory에 자원을 낭비하지 않고 효율적으로 동작하는 고성능 웹 애플리케이션을 개발하는 목적으로 한다. 서비스간 호출이 많은 MSA에 적합한 것이다.

  • spring mvc는 스레드 풀을 만들어서 비동기적으로 여러 스레드를 동시에 실행해도 요청 처리 중 I/O 작업이 필요하면 해당 스레드가 작업이 끝날때까지 기다린다.

  • Spring MVC는 각 요청을 개별 스레드에서 처리합니다. 이 스레드들은 스레드 풀에서 관리되며, 요청 중에 I/O 작업이 필요할 때 해당 스레드는 데이터가 도착하거나 I/O 작업이 완료될 때까지 블로킹 상태가 된다.

  • 이는 스레드가 작업을 기다리는 동안 다른 작업을 수행할 수 없다는 것을 의미하며, 이로 인해 리소스 사용이 비효율적이 될 수 있다.

  • 이와 대비하여, Spring WebFlux비동기적으로 I/O 작업을 처리하여, 스레드가 블로킹되지 않고 다른 요청을 계속 처리할 수 있다.

Spring MVCWebFlux
동기(Synchronous)적인 방식비동기(Asynchronous)적인 방식
블로킹(Blocking) 방식으로 구현논 블로킹(Non-Blocking) 방식으로 구현
명령형 프로그래밍반응형 프로그래밍
JDBC JPA 네트워킹 지원반응형 라이브러리(Reactor, RxJava) 지원

Netty

  • Netty는 프로토콜 서버 및 클라이언트와 같은 네트워크 응용 프로그램을 빠르고 쉽게 개발할 수 있는 NIO (Non-Blocking Input / Output) 클라이언트 서버 프레임 워크이다. TCP 및 UDP 소켓 서버와 같은 네트워크 프로그래밍을 크게 단순화하고 간소화한다.

  • Spring Boot는 기존의 서블릿 기반의 Tomcat을 기반으로 동작한다. 반면 Spring Boot WebFlux는 여러 가지를 고를 수 있지만 기본적으로 Netty를 사용한다. Tomcat은 요청 당 하나의 스레드를 동작하는 반면, Netty는 1개의 이벤트를 받는 스레드와 다수의 Worker 스레드로 동작하게 된다. 조금 더 깊게 살펴보자.

  • Netty는 채널에서 발생하는 이벤트를 EventLoop가 처리하는 구조로 동작하게 된다.

  • 이벤트 루프는 이벤트를 실행하기 위한 무한루프 스레드를 뜻한다.

  • 객체에서 발생한 이벤트는 이벤트 큐에 push되고 이벤트 루프는 이벤트 큐에 입력된 이벤트가 있을 때 해당 이벤트를 꺼내서 실행하게 된다. 이벤트 루프는 스레드의 종류에 따라서 싱글 스레드, 멀티 스레드로 나누어지게 된다.

싱글 스레드

  • 싱글 스레드는 이벤트 루프에서 하나의 스레드로 동작하게 된다. 단일 스레드이기 때문에 예측 가능한 동작을 보장하고, 이벤트 루프의 구현이 단순하다는 장점이 있다. 하지만 하나의 처리 시간이 긴 작업이 들어오면 전체 작업 지연 시간이 늘어난다는 단점이 있다. 그러므로 멀티 코어 CPU 환경에서 CPU 자원을 효율적으로 사용할 수 없다.

멀티 스레드

  • 멀티 스레드는 이벤트 루프에서 이벤트를 처리하는 스레드를 여러개를 두는 모델이다. 싱글 스레드 모델보다는 구현이 복잡해지지만 스레드들이 이벤트 메서드를 병렬로 수행하기 때문에 CPU 자원을 효율적으로 사용할 수 있다. 하지만 여러 이벤트 스레드가 하나의 이벤트 큐에 접근하기 때문에 동시성 이슈 문제가 발생할 수 있다. 또한, 병렬로 처리되기때문에 이벤트의 발생순서와 실행 순서가 일치하지 않는다.

이러한 문제점을 해결하기 위해 아래와 같은 방법으로 해결한다.

  • Netty의 이벤트는 Channel에서 발생
  • 각각의 이벤트 루프 객체는 개인의 이벤트 큐를 가짐
  • Netty Channel은 하나의 이벤트 루프에 등록
  • 하나의 이벤트 루프 스레드에는 여러 채널이 등록

루프들이 이벤트 큐를 공유하여 이벤트 발생순서와 실행 순서가 일치하지 않는다.
Netty는 이벤트 큐를 이벤트 루프 스레드의 내부에 둠으로써 실행 순서의 불일치 원인을 제거하게 된다.

Stream

  • Http 프로토콜 방식은 stateless이기 때문에 클라이언트가 요청을 보내고 server가 응답을 한 번 보내면 연결을 끊어버린다. 그렇게 된다면, 이벤트 루프에서 기억하고 있었던 응답을 다시 보낼 수가 없다.

  • 이러한 문제점을 해결하기 위해 응답을 Stream으로 만들어서 끊기지 않게 계속 유지하는 방법을 SSE 프로토콜 방식이라고 한다.

  • 이벤트 루프와 Stream 같은 SSE 프로토콜 방식으로 반응형 프로그래밍을 할 수 있다.

  • Webflux는 구독과 출판의 개념을 가지고 있는데 구독은 Response가 유지되고 있다. Publish는 유지되고 있는 선으로 계속적으로 데이터를 응답해준다.

  • 다른 사람이 어떤 이벤트를 요청해서 데이터의 변경이 일어나면, 서버가 그 데이터를 바라보고 있는 상대들한테 즉각적으로 Push 해줄 수 있다. 일반적으로 RDBMS는 지원하지 않고 리액티브를 지원하는 MongoDB, Redis를 사용해야하지만 최근에는 R2DBC 라이브러리로 MySQL처럼 RDBMS에서도 비동기 방식으로 연결이 가능하다고 한다.

Mono & Flux

Spring Webflux에서 사용하는 reactive library가 Reactor이고 Reactor가 Reactive Streams의 구현체이다.

Reactor의 주요 객체로 MonoFlux가 있다.

Mono : 한 번 응답하고 끊나는 방식으로 0~1개의 데이터 요청에 대해 응답할 때 사용

  • Reactive Streams의 Publisher 인터페이스를 구현하는 구현체인데, Flux와의 차이점은, Flux는 0~N개의 데이터를 처리하지만, Mono는 0~1개의 데이터를 처리한다.
  • 데이터의 전달처리가 완료되면 onComplete, 데이터를 전달하는 과정에서 오류가 발생하면 onError로 종료된다.
  • 하나의 응답 결과만 리턴하면 되기 때문에 별도로 값은 필요없고, 완료 개념만 있으면 비동기 처리도 표현할 수 있다.
  • 예를 들어, 데이터베이스에서 단일 사용자를 조회하거나 API로부터 하나의 응답을 받는 경우에 사용된다.

Flux : 지속적으로 응답하는 방식으로, 1개 이상의 데이터 요청에 대해 응답할 때 사용

  • Flux는 Reactive Streams에서 정의한 Publisher의 구현체로서, 0~N개의 데이털르 발행(전달, 방출)할 수 있다.
  • 하나의 데이터를 전달할 때마다 onNext 이벤트를 발생한다.
  • Flux내의 모든 데이터의 전달 처리가 완료되면 onComplete 이벤트가 발생하며, 데이털르 전달하는 과정에서 오류가 발생하면 onError 이벤트가 발생한다.
  • 즉, onComplete나 onError가 되기 전까지는 무한 생성가능한 Stream으로 생각해야한다.
  • 예를 들어, 여러 데이터 항목을 처리하거나 여러 이벤트를 연속적으로 받을 때 사용된다.

참고자료

profile
Data Engineer

0개의 댓글