Contents

Spring 5 Reactive 웹개발

스프링 5 리액티브 웹 개발

17년 강의라 현재와는 조금 다른 부분 있을수도

WebFlux 소개

WebFlux의 핵심 = 비동기 논블로킹 방식의 웹 서버 어플리케이션 (구 Spring-Web-Reactive)

메인 테마가 원래는 JDK9 였는데 이제는 WebFlux로 변경되었음. 스프링 리액티브 스택의 웹 파트 담당

용도

효율적으로 동작하는 고성능 웹 어플리케이션 개발 서비스 간 호출이 많은 마이크로서비스 아키텍처에 적합.

잘못 사용하면 애먹기 때문에 조심스럽게 사용..

2가지 개발 방식 지원

  • 기존의 @MVC 방식
  • 새로운 함수형 모델

새로운 요청-응답 모델

  • 서블릿 스택과 API에서 탈피
    • 서블릿 API는 리액티브 함수형 스타일에 적합하지 않기 때문
  • HttpServletRequest, HttpServletResponse -> ServerRequest, ServerResponse

동기 / 비동기

Singleton 은 어떤 숫자가 생각나나? = 1

Dependency Injection 은 어떤 숫자가 생각나나? = 3

  • 두 개의 오브젝트가 있어야 하고
  • 하나의 오브젝트는 다른 오브젝트를 의존하고 있어야 함
  • 의존 관계는 클래스 레벨에서 생길 수 있음
  • 스프링에서 이야기 하는 DI는 오브젝트 간의 의존 관계
  • 이 의존은 런타임에 관계 정의됨
  • 프레임워크가 런타임에 의존관계를 정의하고 의존성을 주입한다

Synchronous / Asynchronous는 어떤 숫자가 생각나나? = 2+

  • Syn == together
  • chrono = time
  • 동기 Synchronous = 두 가지 이상의 존재가 시간을 함께 맞춘다
  • 비동기 Asynchronous = 두 가지 이상의 존재가 시간을 맞추기 않는다
  • 뭐랑 뭐가 동기인가? 혹은 비동기인가? 또 어떤 시간을?

동기

A, B가 시작시간 또는 종료시간이 일치하면 동기

  • A, B 쓰레드가 동시에 작업을 시작하면 동기 (CyclicBarrier)
  • 메소드 리턴(A)시간과 결과를 전달받는(B) 시간이 일치하면 동기

A가 끝나는 시간과 B가 시작하는 시간이 같으면 동기

  • synchronized
  • BlockingQueue

블록킹, 논블록킹

  • 동기, 비동기와는 관점이 다름
  • 내가 직접 제어할 수 없는 대상을 상대하는 방법
  • 대상이 제한적임 - IO, 멀티쓰레드 동기화

대상을 뭉뚱그려 나오는 경우가 많기 때문에 혼동 많이 함

비동기-논블록킹 개발

스프링에서는 어떻게 말해왔나?

Thread pool hell : 서버의 쓰레드 풀과 latency는 밀접한 관계가 있음 (초당 처리율은 떨어짐) 왜 그런가? 블록킹 방식 (특히 I/O) - 요청이 들어오면 서버가 쓰레드 하나를 꺼내서 사용하고 리턴 그 서버에서 응답이 끝나면 ok 그런데 그 서버에서 다른 서버로 요청하는 경우? 계속 대기 대기하는 동안에 CPU는 거의 사용하지 않음. 하지만 쓰레드는 계속 점유하고 있으므로 문제 발생 쓰레드는 서버 입장에서 굉장히 비싼 자원.. 메모리 사용도 많고. 더 많은 요청을 처리하기 위해서는 쓰레드를 무한정 늘릴 수는 없다 (리소스의 한계 때문) 백엔드에 API 요청을 보내고 기다리는 동안에는 쓰레드 자원이 빠르게 고갈됨

Servlet 3.0 - 비동기 요청 처리

  • 서블릿이 어떤 리소스나 이벤트를 기다리는 요청 처리를 완료하지 못하는 경우가 많아지는 문제
  • 대기 상태에 들어가는 성격의 요청 처리라면
    • 비동기 작업 등을 만들고
    • 응답을 생성하지 않은 채로 서블릿을 종료
  • 자원이 확보되거나 이벤트가 발생하면
    • 해당 쓰레드에서 응답 처리하거나 컨테이너에게 전달

2009년에 제안된 Servlet 3.0 똑같이 2009년에 등장한 Node.js - 싱글 쓰레드를 논블로킹으로 처리한다 자바로 개발하던 사람들에게 자바를 쓰지 말자고 제안

Spring 3.2 - 서블릿 3+ 기반 비동기 요청 처리

  • 2012년에 등장
  • 스프링 @MVC에 다양한 방식으로 비동기 요청 처리가 가능한 수단 제공
  • 4.3까지 지속적인 기능 확장
    • Callable
    • DeferredResult
    • ListenableFuture
    • CompletionStage
    • ResponseBodyEmitter

코드로 보는 스프링 비동기 웹 개발 방법의 종류

  • 2개의 API 호출의 조합으로 이루어진 웹 로직
  • 각 API는 1초 정도의 작업 소요
  • 동기 - 블록킹 방식 - 1개
  • 비동기 - 논블록킹 방식 - 4개
    • 명령형 스타일 : ListenableFuture
    • 함수형 스타일 : CompletableFuture
    • 리액티브 : WebFlux @MVC
    • 리액티브 : WebFlux 함수형 모델
  • 서버의 HTTP 요청 처리 쓰레드를 1개로 제한
  • 첫 API를 호출해서 결과를 받아 두번째 API를 호출하고 로직 적용하여 리턴하는 작업
  • 테스트는 200개의 쓰레드를 만들어서 동시에 200개의 요청 전송
  • 각 요청에 대한 처리 결과와 시간, 총 요청 처리시간 확인

비동기 방식에서는 요청 후 거의 바로 리턴 나중에 결과가 필요할텐데, 이 결과를 받기 위해서 특수한 결과 형태 사용

비동기-논블록킹 API 호출 - 함수형 스타일

  • 비동기 API 호출 결과를 CompletableFuture(CompletionStage)로 변환해서 함수형 조합 방식 적용
  • 비동기 작업이 완료됐을 때 의존할 작업을 다음 비동기 작업에 조합하거나 결과 값을 조작하는 방식 구성
  • 간결한 예외 처리, 예외 복구
  • 두 개의 비동기 작업 결과의 결합
  • 각 비동기 작업의 ExecutorService 지정 가능

리액티브 - WebFlux @MVC

  • 스프링 5의 WebFlux 모듈 기반
  • Reactor 리액티브 라이브러리 이용
  • 비동기-논블록킹 리액티브 웹 서버 - 리액티브 웹 클라이언트의 조합
  • 기존 API 호출 방식과 유사한 Mono 방식, 혹은 스트림을 지원하는 Flux 제공

웹의 응답은 3가지 구성을 가짐 - HTTP 상태 코드, 응답 헤더, 응답 body

함수형 스타일 WebFlux

RouterFunction + HandlerFunction

스프링이 웹 요청을 처리하는 방식

  • 요청 매핑
    • 웹 요청을 어느 핸들러에게 보낼지 결정
    • URL, 헤더
    • @RequestMapping
  • 요청 바인딩
    • 핸들러에 전달할 웹 요청 준비
    • 웹 URL, 헤더, 쿠키, 바디
    • 파라미터, 어노테이션 적절히 붙여주면 스프링이 알아서 변환해서 메소드에 넣어줌
  • 핸들러 실행
    • 전달 받은 요청 정보를 이용해 로직 수행하고 결과를 리턴
  • 핸들러 결과 처리 (응답 생성)
    • 핸들러의 리턴 값으로 웹 응답 생성
    • 웹 응답의 3가지 구성

WebFlux 함수형 개발의 구성 요소

RouterFunction

  • 함수형 스타일의 요청 매핑
  • 웹 요청 정보 중에서 URL 경로 패턴 검사

HandlerFunction

  • 요청 바인딩
  • 핸들러 실행
  • 핸들러 결과 처리 (응답 생성)

RouterFunction + HandlerFunction 조합하여 사용

  • RouterFunctions.route(predicate, handler)

RequestPredicate = RequestMapping의 함수형 버전

  • GET(), POST(), PUT(), HEAD()…
  • method()
  • path()
  • contentType()
  • queryParam()
  • accept()

많은 경우 핸들러 (컨트롤러) 로직 복잡함

핸들러 내부 로직이 복잡하다면 분리한다

  • 핸들러 코드만 람다 식을 따로 선언하거나
  • 메소드를 정의하고 메소드 참조로 가져온다

공통의 조건을 컨트롤러 레벨에서 분리할 수 있음 -> nest() 메소드 사용

WebFlux 함수형 스타일의 장점

  • 모든 웹 요청 처리 작업을 명시적인 코드로 작성
    • 메소드 시그니쳐 관례와 타입 체크가 불가능한 어노테이션에 기반하는 @MVC 스타일보다 명확
    • 정확한 타입 체크 가능
  • 함수 조합을 통한 편리한 구성, 추상화에 유리
  • 테스트 작성의 편리함
    • 핸들러 로직은 물론
    • 요청 매핑과 리턴 값 처리까지 단위 테스트로 작성 가능

WebFlux 함수형 스타일의 단점

  • 함수형 스타일의 코드 작성이 편하지 않으면 코드 작성과 이해 모두 어려움
  • 익숙한 방식으로도 가능한데 뭐하러 적용함?

@MVC WebFlux

@Controller + @RequestMapping

기존의 어노테이션 + 메소드 형식의 관례를 이용하는 @MVC 방식과 유사 비동기 + 논블록킹 리액티브 스타일로 작성

ServerRequest, ServerResponse

  • WebFlux의 기본 요청, 응답 인터페이스 사용
  • 함수형 WebFlux의 HandlerFunction을 메소드로 만들었을 때와 유사
  • 매핑만 어노테이션 방식 사용

@MVC 요청 바인딩 = Mono / Flux 리턴 값

  • 가장 대표적인 @MVC WebFlux 작성 방식
  • 파라미터 바인딩은 @MVC 방식 그대로
  • 핸들러 로직 코드 결과를 Mono / Flux 타입으로 리턴

@RequestBody 바인딩 (JSON, XML)

  • T
  • Mono <T>
  • Flux <T>

@ResponseBody 리턴 값 타입

  • T
  • Mono <T>
  • Flux <T>
  • Flux <ServerSideEvent>
  • void
  • Mono <Void>

WebFlux와 리액티브 기술

WebClient + Reactive Data

WebFlux 만으로 성능이 좋아질까?

  • 비동기 - 논블록킹 구조의 장점은 블록킹 I/O를 제거하는 데에서 나온다
  • HTTP 서버에서 논블록킹 I/O는 오래 전부터 지원
  • 뭘 개선해야 할까?

개선할 블록킹 I/O

  • 데이터 엑세스 리포지토리
  • HTTP API 호출
  • 기타 네트워크를 이용하는 서비스

JPA - JDBC 기반 RDB 연결

  • 블로킹 메소드로 점철된 JDBC API
  • 일부 DB는 논블록킹 드라이버가 존재하지만…
  • @Async 비동기 호출과 CFuture를 리액티브로 연결하고 쓰레드풀 관리 통해 웹 연결 자원을 효율적으로 사용하도록 만드는 정도
  • JDK 10에서 Async JDBC가 등장할수도? — R2DBC 등장으로 사용 가능 한듯??

Spring Data JPA의 비동기 쿼리 결과 방식

리포지토리 메소드의 리턴 값을 @Async 메소드처럼 작성

본격 리액티브 데이터 엑세스 기술

  • 스프링 데이터의 리액티브 리포지토리 이용
    • MongoDB
    • Cassandra
    • Redis
    • CouchDB
  • ReactiveCrudRepository 확장

논블록킹 API 호출은 WebClient

  • AsyncRestTemplate의 리액티브 버전
  • 요청을 Mono / Flux 형태로
  • 응답도 Mono / Flux 형태로

비동기-논블록킹 리액티브 웹 어플리케이션의 효과를 얻으려면

  • WebFlux + 리액티브 리포지토리
      • 리액티브 원격 API 호출
      • 리액티브 지원 외부 서비스
      • @Async 블록킹 I/O
  • 코드에서 블록킹 작업이 발생하지 않도록 Flux 스트림 또는 Mono에 데이터를 넣어 전달

리액티브 함수형은 꼭 성능 때문에 사용?

  • 함수형 스타일 코드를 이용해 간결하고 읽기 좋고 조합하기 편한 코드 작성
  • 데이터 흐름에 다양한 오퍼레이터 적용
  • 연산을 조합해서 만든 동시성 정보가 노출되지 않는 추상화된 코드 작성
    • 동기, 비동기, 블록킹, 논블록킹 등을 유연하게 적용
  • 데이터의 흐름의 속도를 제어할 수 있는 메커니즘 제공

논블록킹 I/O에만 효과가 있나?

  • 시스템 외부에서 발생하는 이벤트에도 유용
  • 클라이언트로부터의 이벤트에도 활용 가능

ReactiveStreams

  • WebFlux가 사용하는 Reactor 외에 RxJava2를 비롯한 다양한 리액티브 기술에 적용된 표준 인터페이스
  • 다양한 기술, 서비스 간의 상호 호환성에 유리
  • 자바 9에 Flow API로 포함

뭘 공부해야 하나

  • Java 8 + 함수형 프로그래밍에 익숙해질 것
  • CompletableFuture와 같이 비동기 작업의 조합, 결합에 뛰어난 툴의 사용법을 익힐 것
  • ReactorCore 학습 - Mono/Flux, 오퍼레이터, 스케줄러
  • WebFlux와 스프링의 리액티브 스택 공부
  • 비동기 논블록킹 성능과 관련된 벤치마킹, 모니터링, 디버깅 연구
  • 테스트