Spring/Spring Boot

[spring] WebFlux란 + Reactor 객체란 (Mono<> 와 Flux<>)

민돌v 2022. 12. 7. 02:21

아래의 글 순서대로 읽으시면 해당 글을 이해하시는데 족흠 도움이 됩니다 🙏 (어쩌면 안될지도...^^)

⚙️ Block, Non-Block, sync(동기), Async(비동기) 의 간단한 개념
[Spring] Webclient 란❓ (RestTemplate vs WebClient)
[Spring] WebClient 사용방법 가이드
(now) [spring] WebFlux란 + Reactor 객체란 (Mono<> 와 Flux<>)

 

 

이번 글에서는, Spring webflux 와 reative programing & reactor library 에 대한 개념 이해와 reactor 객체인 mono 와 flux를 다루는 방법의 이해를 목표로합니다

 

[목차]

  1. Spring WebFlux 개념
  2. Spring WebFlux 생긴 이유
  3. Spring WebFlux 장점 및 사용하면 좋은 경우
  4. Netty 란, Netty의 장점
  5. Reactor Library란 (Reactor Promgramming 이란?)
  6. Recator 객체란
  7. 예제 코드

 

📌 1. Spring WebFlux란

Spring WebFlux는 Spring 5 (약 2017년도,,,?) 에서 새롭게 추가된 모듈입니다.

 

WebFlux 란 client, server 에서 reactive 스타일의 어플리케이션의 개발을 도와주는 스프링 모듈입니다.

WebFlux 는 기존의 Servlet API를 기반으로 구축된 웹 프레임 워크인 Spring WebMvc 를 대체할 수 있는 웹 프레임워크입니다.

WebFlux 는 스프링 리액티브 스택의 웹 파트를 담당하고 있고 reactive-stack web framework이며 non-blocking에 Reactive stream을 지원합니다.

 

spring mvc VS spring webflux


 

👏🏻 spring webmvc vs webflux

아주간단하게만 살펴보겠습니다

webmvc

  • Spring MVC는 기본적으로 블럭킹이고 동기방식을 사용합니다.
  • 사용자의 요청이 들어올 때 마다 Thread를 생성하여 처리합니다.
  • 보통은 요청 시 마다 스레드를 생성, 삭제해주면 일정한 리소스가 지속적으로 소모되므로 Thread를 미리 생성해 저장해 두는 Thread Pool 을  생성해 사용합니다.
  • 따라서 spring mvc같은 경우 요청이 들어오면 그 요청을 Queue 에 쌓고 순서에 따라서 Thread 를 하나 점유해 요청을 처리합니다.
  • 동시 다발적으로 스레드 수를 초과하는 요청이 발생한다면 계속해서 요청이 큐에 대기하게되는 Thread Pool Hell 현상이 발생할 수 있습니다.

 

webflux

  • webflux는 이 요청을 처리하는 방식이 Event-Driven 방식이고 비동기 논블러킹 방식입니다.
  • WebFlux는 이벤트 루프가 돌아서 요청이 발생할 경우 그것에 맞는 핸들러에게 처리를 위임하고 처리가 완료되면 callback 메소드 등을 통해 응답을 반환합니다.
  • 이 방식의 경우 요청이 처리될 때까지 기다리지 않기때문에 Spring MVC에 비해 사용자의 요청을 대량으로 받아낼 수 있다는 장점이 있는 것 입니다.

 

 

📌 2. Spring WebFlux를 사용하는 이유

  • 비동기 - 논블럭킹 방식의 리액티브 개발에 사용됩니다.
  • 서버 프로그램이 효율적으로 동작해서, cpu, thread, memory에 자원을 낭비하지않고 효율적으로 동작하는 고성능 웹 어플리케이션을 개발하는 걸 목적으로 합니다.
  • Toby 님의 세미나에서는 서비스간 호출이 많은 마이크로 서비스 아키텍처에 적합하다고 합니다.

 


 

📌  3. WebFlux가 생긴 이유

(1) webflux 가 생긴 이유 중 하나는 "적은 양의 스레드" 와 "최소한의 하드웨어 자원" 으로 동시성을 핸들링하기 위해 만들어졌다고 합니다.

(2) 함수형 프로그래밍이 webflux의 기반이 되었다고 합니다.
Java5에서 Rest controllers나 unit test가 만들어지고, Java8에서는 함수형 API를 위한 Lambda 표현식이 추가되었습니다. 이는 논블로킹 어플리케이션 API의 토대가 되었습니다.

 

➡️ Servlet 3.1 이후부터는 Non-Blocking 을 지원하기는 하지만 일부분만 지원하기 때문에, WebFlux에서는 netty 와 같이 잘만들어진 async, non-blocking 서버를 사용한다고 합니다.

 

따라서 Spring WebFlux는 리엑터 라이브러리 (Reactor library) 와 넷티 (Netty) 를 기반으로 동작합니다.

 


 

🙌🏻 WebFlux 의 또 다른 장점 (사용해볼만한 이유?)

  • Toby 님의 Spring camp. "spring web flux" 컨퍼런스 영상에서는
  • "꼭 비동기적 성능처리 뿐만 아니라, 함수형 코딩만으로도 webflux를 사용할말한 이유가 될 수 있다" 라고 말씀해주십니다.

 

WebFlux 함수형 스타일의 장점

  1. 모든 웹 요청 처리 작업을 명시적인 코드로 작성하게 되어있다.
    • 기존의 Spring MVC 는 어노테이션에 의한 흐름 관례를 사실상 외워야 코드의 해석이 가능하다. (어노테이션의 명시적인 분석이 힘듬)
    • 함수형 타입의 WebFlux는 명시적인 코드로 작성하기 때문에 메소드 시그니처 관례와 타입체크가 불가능한 어노테이션에 의존하는 @MVC 스타일보다는 명확하다 여겨질 수 있습니다.
    • 정확한 타입 체크 가능
  2. 함수 조합을 통한 편리한 구성, 추상화에 유리
    1. 함수형 스타일이기 때문에 추상화와 모듈화에 유리합니다.
    2. 그렇기때문에 MVC 보다는 확정에 유리하다는 장점이 존재합니다.
  3. 테스트 작성의 편리함
    • Spring MVC 는 Controller 나 Service 단위를 테스트하기위해서는 사실상 WebTest 를 진행해야하지만
    • WebFlux는 핸들러 로직은 물론이고 요청 매핑과 리턴 값 처리까지 단위테스트로 작성 가능

 

➡️ 하지만 @MVC 방식에서 바로 WebFlux 로 이전하기에는 거부감이 있으니, 어노테이션을 사용하는 @MVC + webflux 의 비동기 및 논블러킹을 지원하는 방법도 Spring 에서는 지원한다고 합니다.

 


 

📌 4. 넷티와 리액터 라이브러리 (Netty & Reactor Library)

 

1) Netty란 무엇일까

Netty 란 

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

 

Netty 의 장점

  1.  NIO 네트워크 기반 Netty 프레임워크는 비동기식 이벤트 기반 네트워킹 (Event Driven) 을 지원합니다.
  2. Tomcat서버가 10,000건의 커넥션을 처리한다면, netty는 NIO 방식으로 이벤트를 처리하기 때문에 자원이 스레드를 계속 점유하며 Block 상태를 유지 하지 않습니다.
    때문에  100,000건에서 1,000,000건의 커넥션을 처리할 수 있는 장점이 있습니다.
  3. 네티의 경우에는 이벤트 기반 방식으로 동작하기 때문에  톰캣과달리 스레드Pool의 스레드 개수는 머신의 Core 개수의 두배입니다
  4. 즉! , 스레드수가 작다 -> 스레드 레이싱, 경합이 잘 일어나지 않는다

webflux event 동작기반 네트워킹 흐름 - webflux nio 스레드 처리

 

Netty 의 구조

netty 의 핵심 컴포넌트로는 Channel, CallBack, Future, 이벤트와 핸들러, Event Loop와 PipeLine이 있습니다.

 

Channel : 하나 이상의 입출력 작업을 수행할 수 있는 하드웨어 장치, 파일, 네트워크 소켓이나 프로그램 컴포넌트와 같은 Open된 Connection 을 의미합니다.

CallBack : 다른 메서드로 자신에 대한 참조를 제공하는 메서드입니다. 이벤트를 처리할 때 Netty 내부적으로 콜백을 이용하는데, 이때 ChannelHandler 인터페이스를 통해 이벤트를 처리합니다.

Future : 작업이 완료가 될 경우 애플리케이션에 알립니다. Future 객체는 비동기 작업의 결과를 담는 Plachloder의 역할을 합니다. 이때 ChannelFuture 인터페이스를 이용해 결과값을 활용합니다.

Event와 Handler : Netty는 작업 상태의 변화를 알리기 위해 이벤트를 이용하고, 발생한 이벤트를 기준으로 Handler를 통해 트리거합니다.

 

Event Loop

  • 유저가 입출력을 요구할 경우의 흐름

 

PipeLine

  • 이벤트 루프에서 이벤트를 받아 핸들러에 전달하는 역할

 

Netty 핵심 플로우

 


 

2) Reactor Library 란 무엇일까

그렇다면 인제 리액티브 프로그래밍이란??? 리액터 라이브러리란 무엇일까

 

1. 리액티브 프로그래밍이란 ❓ (Reactive Programming)

위키피디아에는 아래와 같이 정리하고 있습니다.

  • 리액티브 프로그래밍은 데이터 스트림과 변경 사항 전파를 중심으로하는 비동기 프로그래밍 패러다임이다.
  • 이것은 프로그래밍 언어로 정적 또는 동적인 데이터 흐름을 쉽게 표현할 수 있어야하며, 데이터 흐름을 통해 하부 실행 모델이 자동으로 변화를 전파할 수 있는 것을 의미한다.

 

혹은

 

리액티브 프로그래밍이란, 변화의 전파 데이터 흐름과 관련된 선언적 프로그래밍 패러다임이다. 

  • 변화의 전파와 데이터 흐름 : 데이터가 변경 될 때 마다 이벤트를 발생시켜서 데이터를 계속적으로 전달한다.
  • 선언적 프로그래밍 : 실행할 동작을 구체적으로 명시하는 명령형 프로그램이과 달리 선언형 프로그래밍은 단순히 목표를 선언한다.

 

👏🏻 간단하게 지금까지 공부한데로 정리해보자면

리액티브 프로그래밍이란, 데이터 스트림을 이용하여 데이터를 전달하고, 데이터의 변경 시점을 이벤트로하여 (or Call Back)  수신자와 송신자 사이에 데이터를 전달시키는 비동기 프로그래밍 정도로 이해가 되는것 같습니다 (제 주관적인 정리..ㅎ)

"Reactive Programming is Programming with asynchronous data streams"

 

리액티브 프로그래밍을 하면 다음과 같은 이점을 얻을 수 있습니다.

  •   간결해진 Thread 사용
  •   간단한 비동기 연산
  •   콜백 지옥의 제거

데이터스트림과 변경사항을 토대로 비동기적인 프로세스를 가지는 Reactive Programming (사진출처 : https://m.blog.naver.com/jdub7138/220983291803)

 

 

 

저는 개념을 잡기위한 정리여서,, 더 자세한 내용은 아래 블로그나, 다른 문서를 참고해주세요!

https://m.blog.naver.com/jdub7138/220983291803

https://als2019.tistory.com/71

https://gngsn.tistory.com/223


 

2. 리액터 라이브러리란 ❓ (Reactor Library)

Reactor 란 Pivotal의 오픈소스 프로젝트로, JVM 위에서 동작하는 Non-Blocking 어플리케이션을 만들기 위한 리액티브 라이브러리입니다.

 

Reactor는 RxJava 2 와 함께 Reactive Stream 의 구현체이기도 하고, Spring Framwork 5부터 리액티브 프로그래밍을 위해 지원되는 라이브러리 입니다.

 

👏🏻 Mono 와 Flux 란

  • 리액터는 리액티브 스트림을 구현하는 라이브러리로 Mono 와 Flux 2가지 데이터 타입으로 스트림을 정의합니다!
Reactor is the reactive library of choice for Spring WebFlux. It provides the Mono and Flux API types to work on data sequences of 0..1 (Mono) and 0..N (Flux)

즉, Spring WebFlux 를 사용하여 비동기적인 데이터 스트림의 처리를 리액터 라이브러리가 제공하는 데이터 타입인 Mono 와 Flux 로 다뤄야한다는 것 인데

"그렇기 때문에 WebFlux 에서는 모든 응답을 Mono 혹은 Flux 에 담아서 반환해주어야합니다."

이 2 데이터 타입은 Stream을 정의하기 때문에, Reactor는 최소 Java8에서 동작하며 Java8의 피쳐를 잘 지원한다는 특징이 있습니다.

 

 

👏🏻 Mono 와 Flux 의 차이점

Mono는 0-1개의 결과만을 처리하기 위한 Reactor의 객체이고
Flux는 0-N개인 여러 개의 결과를 처리하는 객체입니다.

 

Reactor를 사용해 일련의 스트림을 코드로 작성하다 보면 보통 여러 스트림을 하나의 결과를 모아줄 때 Mono를 쓰고, 각각의 Mono를 합쳐서 여러 개의 값을 여러 개의 값을 처리하는 Flux로 표현할 수도 있습니다.

 

Mono <>

  • Mono 는 0 또는 하나의 데이터 항목과 에러를 가집니다.

Mono 객체

Flux <>

  • Flux 는 0 또는 하나 이상의 데이터 항목과 에러를 가집니다.

flux 객체

 

Mono와 Flux모두 Reactive Stream의 Publisher 인터페이스를 구현하고 있으며, Reactor에서 제공하는 풍부한 연산자들(operators)의 조합을 통해 스트림을 표현할 수 있습니다. (매우 큰 장점)

예를 들어 Flux에서 하나의 결과로 값을 모아주는 reduce연산자는 Mono를 리턴하고, Mono에서 flatMapMany라는 연산자를 사용하면 하나의 값으로부터 여러 개의 값을 취급하는 Flux를 리턴할 수 있습니다.

그리고 Publisher인터페이스에 정의된 subscribe메서드를 호출함으로써 Mono나 Flux가 동작하도록 할 수 있습니다.

 


 

WebFlux 사용 예제 코드는 ✨깃허브 에 있습니다

 

다음 게시글에서 코드를 조금 더 뜯어보고 기록해보겠습니다~!

(시간이 된다면..ㅎㅎ)

 

 

 

 

 

 


💡 참고