Java/WebFlux

[Java] Spring Boot Webflux 이해하기 -1 : 흐름 및 주요 특징 이해

adjh54 2023. 8. 9. 22:46
728x170
해당 글에서는 Spring Boot Webflux에 대해 이해하고 전체적인 흐름, 특징에 대해서 이해를 돕기 위해 작성한 글입니다.

 

 

 

💡 [참고] Spring WebFlux 관련 글에 대해 궁금하시면 아래의 글을 참고하시면 도움이 됩니다.
분류 링크
Spring Boot Webflux 이해하기 -1 : 흐름 및 주요 특징 이해 https://adjh54.tistory.com/232
Spring Boot Webflux 이해하기 -2 : 활용하기 https://adjh54.tistory.com/233
Spring Boot WebFlux 이해하고 구현하기 -1 : 반응형 프로그래밍에서 WebFlux까지 흐름 https://adjh54.tistory.com/627
Spring Boot WebFlux 활용하여 구현하기 -2: 계층구조 및 활용예시 https://adjh54.tistory.com/628
Spring Boot WebFlux 활용하여 구현하기 -3: Publisher/Subscriber 데이터 처리 타입, 에러 핸들러, 백프레셔 https://adjh54.tistory.com/629
Spring Boot WebFlux 활용 Github https://github.com/adjh54ir/blog-codes/tree/main/spring-boot-webflux

 

 

1) Spring Boot Webflux


💡 Spring Boot Webflux

- 반응형 및 비동기적인 웹 애플리케이션 개발을 지원하는 모듈입니다. 이 모듈은 Reactive Streams 사양을 기반으로 하여, 비동기적인 이벤트 지향 프로그래밍을 통해 높은 확장성과 성능을 제공합니다.

- Webflux를 사용하는 목적은 반응형 프로그래밍을 통해 '높은 처리량'과 '확장성'을 갖는 애플리케이션을 만드는 것을 목표로 합니다.
[출처] Guide to Spring 5 WebFlux | Baeldung

 
 
 

[ 더 알아보기 ]

💡 Webflux는 Spring Framework 5.0부터 지원한다고 들었는데 Spring Boot는 몇 버전이 되어야 하는가?

- Spring Framework 5.0은 Spring Boot 2.0 이상을 요구하므로 Webflux를 사용하는데 Spring Boot 2.0 이상 버전이 최소 요구사항이 됩니다.

 
 

2) Webflux 주요 특징


💡 Webflux는 ‘반응형 프로그래밍(Reactive Programming)’을 구현하기 위해 Reactor라는 라이브러리를 이용합니다. Reactor는 Netty 서버를 통해 비동기식 이벤트 기반의 서버를 환경을 제공하며 이를 이용하여 비동기 방식의 논 블로킹 요청을 통해 이벤트 기반의 반응형 스트림으로 데이터를 주고받습니다.

 
 

1. 반응형 프로그래밍(Reactive Programming)


💡 반응형 프로그래밍(Reactive Programming)

- Spring Webflux는 반응형 프로그래밍(Reactive Programming) 방식을 통해 ‘이벤트 기반의 비 동기식 애플리케이션’을 구축할 수 있습니다.
- 이 이벤트는 ‘비 동기적’으로 처리되며 새로운 이벤트가 발생하면 이벤트 스트림이 생성이 되며 스트림을 구독하면 이벤트를 처리할 수 있습니다.

 

 

[ 더 알아보기 ]

💡 프로그래밍 패러다임으로 본 명령형 프로그래밍 vs 반응형 프로그래밍

- 명령형 프로그래밍의 경우는 컴퓨터가 수행해야 하는 일을 명령의 목록으로 나열을 하고 이를 순서대로 실행합니다. 반응형 프로그래밍의 경우는 데이터 스트림을 처리하며 이 데이터 스트림이 변경될 때마다 반응하는 것을 의미합니다.
분류 명령형 프로그래밍 반응형 프로그래밍
처리 방식 명령어를 순서대로 실행 데이터 스트림을 처리하며, 데이터 변화에 반응
동기/비동기 대부분 동기적 대부분 비동기적
코드 구성 명령형 코드 선언형 코드
사용 예시 계산기, 루프 등 이벤트 처리, UI 업데이트 등
프로그래밍 종류 Spring MVC Webflux

 

💡 [참고] 프로그래밍 패러다임에 대해 궁금하시면 아래의 글을 참고하시면 도움이 됩니다.
 

[Java] 프로그래밍 패러다임 이해하기

해당 글에서는 프로그래밍 패러다임에 대해서 이해하고 각각의 용어에 대해서 이해를 돕기 위해서 작성한 글입니다. 1) 프로그래밍 패러다임 💡 프로그래밍(Programming) 이란? - 하나 이상의 관련

adjh54.tistory.com

 
 
 

2. Reactor


💡 Reactor

- 반응형 프로그래밍(Reactive Programming)을 구현하기 위한 Reactor는 Reactive 라이브러리 중 하나입니다.
- Publisher-Subscriber 패턴을 중심으로 동작하며 데이터를 생성하고 가공하고 구독자에게 전달하는 역할을 합니다.
- Reactor에서는 Mono와 Flux의 데이터 스트림 유형을 지원합니다.

 

2.1. Publisher-Subscriber 패턴: 반응형 스트림(Reactive Stream)

💡 반응형 스트림(Reactive Stream)

- 비 동기적 및 이벤트 기반 응용 프로그램을 위한 ‘스트림 처리 기술’을 의미합니다. 해당 기술의 핵심은 ‘Publisher’가 ‘Subscriber’에게 데이터를 제공하는 것을 의미합니다.

- 이는 Publisher는 데이터를 생성하고 Subscriber는 이를 처리합니다. 이러한 방식으로 스트림을 처리함으로써 데이터를 더 효율적으로 처리할 수 있습니다.
객체 설명
Publisher : 발행자 데이터를 생성하고, Subscriber에게 전송합니다.
Subscriber : 구독자 Publisher로부터 데이터를 받아들이고, 소비합니다.
Subscription : 구독 Subscriber가 처리할 데이터의 양을 정의합니다.

 
 
 

2.2. 반응형 스트림(Reactive Stream) 수행과정

💡 반응형 프로그래밍에서 데이터 처리를 수행하는 반응형 스트림의 처리과정에 대해 이해합니다.

💡 반응형 스트림 처리 과정

1. [subscribe] Subscriber를 Publisher에 ‘등록’하고 데이터 스트림을 ‘수신할 준비’가 되었음을 Publisher에게 알립니다.

2. [onSubscribe] Publisher가 Subscriber에게 데이터 스트림을 ‘전송하기 시작하기 전에 호출’됩니다. 이 메서드를 통해 Subscriber는 Subscription 객체를 받아들여 데이터의 양을 제어할 수 있습니다.

3. [request(n)/cancel] request(n) 메서드는 Publisher에게 n개의 데이터를 요청하고, cancel 메서드는 데이터 스트림을 취소합니다.

4. [onNext(data)] Publisher가 ‘생성한 데이터’를 Subscriber에게 전달합니다. 이 메서드는 데이터가 전송될 때마다 호출됩니다.

5. [onComplete/onError] Publisher가 모든 데이터를 전송하고, 더 이상 데이터가 없을 때 호출됩니다.

- onComplete 메서드는 모든 데이터가 성공적으로 전송되었음을 나타냅니다.
- onError 메서드는 데이터 전송 중 오류가 발생했음을 나타냅니다.

 
 

[ 더 알아보기 ]

💡 이벤트(Event)

- 구독자(Subscriber)가 발행자(Publisher)에게 데이터를 요청하고 발행자가 데이터를 생성하고 전송하는 것을 의미합니다. 구독자가 요청을 한 후 발행자는 데이터를 생성하고 onNext(data) 메서드를 사용하여 구독자에게 데이터를 전달합니다. 구독자는 request(n) 메서드를 사용하여 발행자에게 추가 데이터를 요청할 수 있습니다.


💡 백프레셔(BackPressure)

- 비동기 스트림 처리에서 ‘데이터의 양을 제어’하는 것을 의미합니다. 발행자(Publisher)가 생성한 데이터를 구독자(Subscriber)가 처리할 때 구독자가 처리할 수 있는 데이터 양을 초기화하여 데이터가 생성되는 상황을 방지하기 위해 백 프레셔를 사용합니다.

- 백프레셔는 Subscriber가 처리할 데이터의 양을 Subscription을 통해 제어하며, Publisher는 Subscriber의 처리 속도에 맞춰 데이터를 생성합니다. 이를 통해 Subscriber가 처리할 수 있는 양 이상의 데이터가 생성되는 상황을 방지하고, 시스템의 안정성을 보장할 수 있습니다.

 
 

2.3. Mono

💡 Mono

- Reactor 라이브러리에서 제공하는 Reactive Streams의 Publisher 중 하나로 오직 ‘0개 또는 하나의 데이터항목 생성’하고 이 결과가 생성되고 나면 스트림이 종료되면 결과 생성을 종료합니다.
- Mono를 사용하여 비동기적으로 결과를 반환하면 해당 결과를 구독하는 클라이언트는 결과가 생성될 때까지 블로킹하지 않고 다른 작업을 수행할 수 있습니다.
메소드 설명
just() 주어진 데이터를 포함하는 Mono를 생성합니다.
empty() 데이터가 없는 Mono를 생성합니다.
error() 에러 상황을 나타내는 Mono를 생성합니다.
fromCallable() Callable 객체를 이용해 Mono를 생성합니다.
fromFuture() Future 객체를 이용해 Mono를 생성합니다.
fromRunnable() Runnable 객체를 이용해 Mono를 생성합니다.

 
 

💡 [ Mono 사용 예시 ]

- "Hello, world!"라는 문자열을 Mono.just를 통해 Mono로 만든 후, map 연산자를 이용해 문자열을 대문자로 변환합니다.
- 이후, flatMap 연산자를 이용해 "Mono: "이라는 문자열과 결합합니다.
- 마지막으로, 비동기적으로 처리된 결과값을 subscribe 메서드를 이용하여 출력합니다.
Mono.just("Hello, world!")
    .map(String::toUpperCase)
    .flatMap(s -> Mono.just("Mono: " + s))
    .subscribe(System.out::println);

 
 

2.4. Flux

💡 Flux

- Reactor 라이브러리에서 제공하는 Reactive Streams의 Publisher 중 하나로 Mono와 달리 ‘여러 개의 데이터 항목’를 생성하고 스트림이 종료되면 결과 생성을 종료합니다.

- 비동기 작업을 수행하면 작업이 완료될 때까지 블로킹하지 않고 다른 작업을 수행할 수 있습니다.
- Spring WebFlux에서 Flux를 사용하여 HTTP 요청을 처리하는 경우, 요청을 수신한 즉시 해당 요청을 처리하고 결과를 생성하는 대신 결과 생성이 완료될 때까지 다른 요청을 처리할 수 있습니다.
메소드 설명
just() 주어진 데이터를 포함하는 Flux를 생성합니다.
fromIterable() Iterable 객체를 이용해 Flux를 생성합니다.
fromArray() 배열을 이용해 Flux를 생성합니다.
fromStream() Stream을 이용해 Flux를 생성합니다.
empty() 데이터가 없는 Flux를 생성합니다.
error() 에러 상황을 나타내는 Flux를 생성합니다.
range() 지정된 범위의 정수를 포함하는 Flux를 생성합니다.
interval() 일정 시간 간격으로 값을 생성하는 Flux를 생성합니다.
merge() 여러 개의 Flux를 병합하여 하나의 Flux로 만듭니다.
concat() 여러 개의 Flux를 순차적으로 이어붙여 하나의 Flux로 만듭니다.
zip() 여러 개의 Flux를 조합하여 튜플 형태로 만듭니다.
collectList() Flux에 포함된 모든 데이터를 리스트로 수집합니다.
collectMap() Flux에 포함된 모든 데이터를 맵으로 수집합니다.

 
 

💡 [ Flux 예시 ]

- "apple", "banana", "cherry" 세 문자열을 Flux.just를 통해 Flux로 만든 후, map 연산자를 이용해 문자열을 대문자로 변환합니다.
- 이후, flatMap 연산자를 이용해 "Flux: "이라는 문자열과 결합합니다.
- 마지막으로, 비동기적으로 처리된 결과값을 subscribe 메서드를 이용하여 출력합니다.
Flux.just("apple", "banana", "cherry")
    .map(String::toUpperCase)
    .flatMap(s -> Mono.just("Flux: " + s))
    .subscribe(System.out::println);

 
 

3. Netty


💡 Netty

- 자바 기반의 네트워크 애플리케이션 프레임워크로 ‘비동기식 이벤트 기반 서버’를 만드는 데 사용이 됩니다.
- 높은 성능과 확장성을 갖추고 있으며, 다양한 프로토콜을 지원하고 있어 네트워크 애플리케이션 개발에 매우 유용합니다.

 

 [ 더 알아보기 ]

💡 Netty와 Tomcat의 비교
특성 Netty Tomcat
아키텍처 이벤트-기반 요청 당 쓰레드
성능 비동기 처리로 인한 높은 성능 동기 처리로 인한 상대적으로 느린 성능
확장성 다수의 동시 접속 처리가 가능한 높은 확장성 쓰레드 개수 제한으로 확장성 한계
프로토콜 지원 HTTP, WebSocket, TCP 등 다수의 프로토콜 지원 주로 HTTP에 특화
유연성 다양한 애플리케이션에 사용 가능한 높은 유연성 웹 애플리케이션에 특화된 제한된 유연성
사용 편의성 이벤트-기반 아키텍처로 인한 학습 곡선이 높음 전통적인 서블릿 기반 아키텍처에 익숙한 개발자에게 사용하기 편리

 
 
 
 

4. Blocking Request / Non-Blocking Request


💡 일반적으로 Webflux를 사용하는 경우 Non-Blocking Request를 사용하는 것이 좋습니다. Blocking Request를 사용하면 스레드가 차단되어 성능이 저하되기 때문입니다.
💡 WebFlux는 Reactive Programming 모델을 위해 설계되었으며, Non-Blocking Request를 사용하면 이러한 모델을 더욱 효과적으로 활용할 수 있습니다.

 
 

💡 Blocking Request (=Synchronous request)

- 기존 Spring MVC에서 사용되던 Blocking Request를 수행하면서 클라이언트에서 요청을 보내면 결과가 반환될 때까지 대기를 하는 것을 의미합니다.

출처 https://reflectoring.io/getting-started-with-spring-webflux/

 

💡 Non-Blocking Request(=Asynchrouse Request)

- webflux에서 제공하는 Non-Blocking Request을 수행하면서 요청을 보내고 결과가 반환되지 않더라도 다른 작업을 수행할 수 있는 것을 의미합니다.

출처 https://reflectoring.io/getting-started-with-spring-webflux/

 
 
 
 

5. Multi Event Loop


💡 Multi Event Loop

- Reactor Core에서 지원을 하며 단일 스레드로 동작하는 ‘이벤트 루프를 여러 개 사용’하는 것을 의미합니다. 이를 통해 블로킹 I/O 작업이 발생하더라도 다른 이벤트 루프를 통해 애플리케이션이 멈추지 않고 계속 동작할 수 있습니다.

 
 
 

💡 Multi Event Loop 처리 과정

1. [Event Queue] 이벤트가 발생하면 해당 이벤트를 처리하기 위한 콜백 함수를 저장합니다.

2. [Process Events] 이벤트 루프(Event Loop)는 이벤트 큐에서 이벤트를 하나씩 가져와 처리합니다.

3. [Event Loop] 이벤트 루프는 주어진 작업을 처리합니다.

4. [Register Callback] 작업을 처리하기 위한 콜백 함수를 등록합니다.

5. [Intensive Operation(Platform)] 블로킹 I/O 작업이 발생하면, Event Loop는 해당 작업을 처리하기 위한 새로운 스레드를 생성합니다.

6. [Operation Completion] 블로킹 I/O 작업이 완료되면, 결과를 Event Loop에게 알리고 새로운 스레드는 종료됩니다.

7. [Trigger Callback] Event Loop는 결과를 처리하고, 다른 작업을 처리합니다.

 
 

 

 

💡 [참고] Java에서 외부 통신을 하는 방법들에 대해 궁금하시면 아래의 글을 참고하시면 도움이 됩니다
분류 주제 링크
RestTemplate Spring Boot Web 활용 : RestTemplate 이해하기 https://adjh54.tistory.com/234
WebClient Spring Boot Webflux 이해하기 -1 : 흐름 및 주요 특징 이해 https://adjh54.tistory.com/232
WebClient Spring Boot Webflux 이해하기 -2 : 활용하기 https://adjh54.tistory.com/233
Open Feign Spring Cloud OpenFeign 이해하고 활용하기 -1 : 주요 개념 및 환경 구성, 활용 예시 https://adjh54.tistory.com/616
Github 외부 통신의 활용 방법을 담은 예제 Repository https://github.com/adjh54ir/blog-codes/tree/main/spring-boot-external-network

 

 

3) WebClient


💡 WebClient

- WebFlux의 일부인 Webclient는 비동기적인 방식으로 HTTP 요청을 보내고 응답을 받을 수 있는 라이브러리를 의미합니다.
- 다수의 외부 API 호출이나, 다른 서비스들과의 통합 작업에서 유용합니다.
- WebFlux의 WebClient는 비동기적인 방식으로 HTTP 요청을 보내고 응답을 받을 수 있는 라이브러리입니다. 이를 통해 Reactive Streams를 이용하여 높은 성능의 네트워크 통신을 구현할 수 있습니다.
WebClient webClient = WebClient.builder().build();

Mono resultMono = webClient.get()
        .uri("<https://www.example.com/>")
        .retrieve()
        .bodyToMono(String.class);

String result = resultMono.block();

System.out.println(result);

 
 

[ 더 알아보기 ]

💡 Spring MVC 환경에서 WebClient를 사용해도 될까?

- Spring MVC는 WebFlux와 달리 블로킹 I/O를 사용하기 때문에, 동기적인 작업을 수행할 때는 WebClient보다 RestTemplate을 사용하는 것이 더 적합합니다. 하지만 비동기적인 작업에서는 WebClient를 사용하는 것이 더욱 효율적입니다.

💡 WebClient, RestTemplate와 같은 HTTP 클라이언트들 외에 다른 것도 있는가?
- OkHttp, Retrofit, Feign와 같은 것들이 있습니다.

 

💡 RestTemplate와 WebClient 비교
메소드 설명
just() 주어진 데이터를 포함하는 Flux를 생성합니다.
fromIterable() Iterable 객체를 이용해 Flux를 생성합니다.
fromArray() 배열을 이용해 Flux를 생성합니다.
fromStream() Stream을 이용해 Flux를 생성합니다.
empty() 데이터가 없는 Flux를 생성합니다.
error() 에러 상황을 나타내는 Flux를 생성합니다.
range() 지정된 범위의 정수를 포함하는 Flux를 생성합니다.
interval() 일정 시간 간격으로 값을 생성하는 Flux를 생성합니다.
merge() 여러 개의 Flux를 병합하여 하나의 Flux로 만듭니다.
concat() 여러 개의 Flux를 순차적으로 이어붙여 하나의 Flux로 만듭니다.
zip() 여러 개의 Flux를 조합하여 튜플 형태로 만듭니다.
collectList() Flux에 포함된 모든 데이터를 리스트로 수집합니다.
collectMap() Flux에 포함된 모든 데이터를 맵으로 수집합니다.

 
 
 
 

💡 [참고] 해당 글은 다음글에서 이어집니다.
 

[Java] Spring Boot Webflux 이해하기 -2 : 활용하기

해당 페이지에서는 Spring Boot Webflux를 이용하여 실제 구현하고 활용하는 방법과 WebClient를 이용한 다른 도메인 호출 방법에 대해 공유합니다. 1) 주요 구성요소 확인 💡 개발 환경을 구성하기 이

adjh54.tistory.com

 
 
 
오늘도 감사합니다. 😀
 
 
 
 

그리드형