[Spring] WebFlux란?

이진영·2024년 1월 18일
0

Spring

목록 보기
4/4

WebFlux란?

Spring5에서 새롭게 추가된 Reactive-stack의 웹 프레임워크이며, 클라이언트/서버에서 리액티브(reactive) 애플리케이션 개발을 위한 논블로킹 리액티브 스트림을 지원한다.

개요

  1. 기존의 이용자들은 적은 수의 스레드로 동시성을 처리하고 더 적은 하드웨어 리소스로 확장 할 수 있는 웹 스텍의 요구사항으로 인한 발전!!

  2. 함수형 프로그래밍의 발전 기존에 람다 식을 추가하면 java의 기능적 API에 대한 기회가 생겼다.


[기존 방식에 대한 문제]

원래는 우리는 무심결에 비동기를 @Async를 통한 구현을 한적이 있을 것이다. 하지만 이는 진짜 비동기가 아니였던 점...문제점은 아래와 같다.

Spring Framework에 포함된 웹 프레임워크인 Spring Web MVC는 Servlet Api 및 Servlet 컨테이너 용으로 특별히 구축되어 있는 구조를 뛰었습니다.

그렇기에 구조적으로 문제점이 있었다. 동기적으로 처리되는 모듈(Filter, Servlet)과 블로킹 방식의 API들이 있기에 완벽한 논 블로킹 환경의 개발을 할 수 없었다.

📌 blocking : 작업을 마칠 때까지 기다리는 현상을 blocking이라한다.

반면 Spring5로 넘어가면서 기술이 발전함에 있어 Spring에서도 Reactive Streams을 이용해 반응형 스텍 웹 프레임워크인 Spring Flux를 도입하게 되었습니다.


그럼 무엇을 지원하는가?

Reactive Streams

비동기식 스트림 처리에 대한 표준을 제공하기 위한 계획입니다. 여기서 계획에 달성하기 위한 공식 스펙은 non-blocking , backPressure이 있고 이러한 스펙을 통해서 Reactive Streams의 목표 및 범위를 지정할 수 있습니다.

📌 non-blocking : 다른 주체의 작업에 관련없이 자신의 작업을 하는 것을 의미한다. 즉, client가 server에게 요청을 보내고 응답을 받야하지만 client가 계속 다른 작업을 할 수 있게끔 해준다.

Reactive Streams Goals, Design and Scope

비동기 경계를 명확히하여 스트림 데이터 교환을 효과적으로 관리하는것에 있습니다.

효과적으로 관리함에이 있어 가장 큰 문제점은 데이터 처리가 목적지의 리소스 소비스를 예측가능한 범위에서 신중하게 제어할 수 있어야 한다. 이러한 관리는 중요한 backpressure에서 관리하게 된다.

backpressure은 무엇인가?

스트림 요소의 전송을 조절하는 방법도를 제공하는 역할을 한다. 즉 수신자가 소비할 수 있는 요소 수를 제어하게 된다.

스프링에서 제공하는 예시는 아래와 같다.

  • 시스템에는 게시자(Publisher), 소비자(Consumer) 및 그래픽 사용자 인터페이스(GUI)의 세 가지 서비스가 포함되어 있다.
  • 게시자(Publisher)는 소비자(Consumer)에게 초당 10000개의 이벤트를 보낸다.
  • 소비자(Consumer)는 이를 처리하고 결과를 GUI로 보낸다.
  • 소비자(Consumer)는 초당 7500개의 이벤트만 처리할 수 있습니다.

이 속도에서는 소비자가 이벤트(배압)를 관리할 수 없다. 그렇기에 시스템이 무너지고 사용자는 결과를 볼 수 없는 현상으로 이어지게 된다.


spring에서 backpressure 전략

spring에서는 기존에 배압에 대해서 설명한 문제점을 인지하고 이를 해결하기 위한 여러 전략과 메소드를 제공합니다.

onErrorReturn : 오류 발생시 대체 값을 제공

Flux<String> flux = Flux.just("A", "B", "C", "D")
    .map(s -> {
        if (s.equals("C")) {
            throw new RuntimeException("An error occurred!");
        }
        return s.toLowerCase();
    })
    .onErrorReturn("error");

flux.subscribe(System.out::println);

onErrorResume : 오류 발생시 fallback stream 제공

Flux<String> flux = Flux.just("A", "B", "C", "D")
    .map(s -> {
        if (s.equals("C")) {
            throw new RuntimeException("An error occurred!");
        }
        return s.toLowerCase();
    })
    .onErrorResume(e -> Flux.just("error"));

flux.subscribe(System.out::println);

retry : error 발생시 stream 다시 시도

Flux<String> flux = Flux.just("A", "B", "C", "D")
    .map(s -> {
        if (s.equals("C")) {
            throw new RuntimeException("An error occurred!");
        }
        return s.toLowerCase();
    })
    .retry(3);

flux.subscribe(System.out::println);

하지만 그럼에도 고질적인 문제를 해결을 못할 수도 있다 그렇기 Spring에서는 아래와 같은 문제해결 방안을 고려하라고 권장한다.

  • 전송된 데이터 스트림을 제어 : 게시자(Publisher)는 이벤트 속도를 늦춰야 하는 의미를 담겨있다. 즉 이벤트의 갯 수를 다소 적게 설정해서 소비자(Consumer)가 과부화 되지 않게 설정하는 것이다.
  • 추가 데이터 양을 버퍼렁 문제 : 소비자는 나머지 이벤트를 처리할 수 있을 때까지 일시적으로 저장한다.(메모리 충돌을 일으키는 경우 버퍼 바인딩을 해제)
  • 추적할 수 없는 추가 이벤트는 삭제

<참고자료>
https://docs.spring.io/spring-framework/reference/web/webflux/new-framework.html
https://colevelup.tistory.com/40
https://www.baeldung.com/spring-webflux-backpressure
https://recordsoflife.tistory.com/1329
https://docs.spring.io/spring-framework/reference/web/webflux/new-framework.html

profile
내가 공부한 것들을 적는 공간

0개의 댓글