1.1 Overview
- Spring WebFlux 개요
- Spring 5.0 에 새로 등장한 웹 프레임워크 + 리액티브 스택
- Java8 이상에서 사용 가능
- 초기 이름은 Spring Web Reactive에서 Spring WebFlux로 변경
- 용도
- 비동기-논블록킹 리액티브 개발에 사용
- 효율적으로 동작하는 고성능 웹 애플리케이션 개발
- 서비스간 호출이 많은 마이크로서비스 아키텍처에 적합
- Asynchronous
- Non blocking I/O (NIO)
IO는 라인을 읽을때까지 블록되며, NIO는 채널을 통해 데이터를 읽고 주기적으로 버퍼를 체크합니다.
IO는 함수를 호출 했을 때 제어권이 넘어가고 block되며 함수가 결과값을 리턴
NIO는 함수를 호출 했을 때 제어권이 넘어가지 않고 다른 작업을 하다가 결과값을 리턴
- Sample Code
/* 동기, 블록킹 API 호출 */
@GetMapping("/orders")
public UserOder orders(String email) {
try {
User user = findUserApi(email);
List<Order> orders = getOpenOders(user);
return new UserOder(email, orders);
} catch (Exception e) {
return UserOder.FAIL;
}
}
@GetMapping("/orders")
/* 비동기, 논블록킹 API호출 - 콜백 */
public DeferredResult<UserOrder> asyncOrders(String email) {
DeferredResult dr = new DeferredResult();
ListenalbleFuture<UserOrder> f1 = asyncFindUserApi(email);
f1.addCallback(user -> {
ListenalbleFuture<UserOrder> f2 = asyncFindUserApi(user);
f2.addCallback(orders -> {
dr.setResult(new UserOrder(email, orders));
}, e -> {
dr.setErrorResult(UserOrder.FAIL)
});
}, e -> {
dr.setErrorResult(UserOrder.FAIL)
}
);
return dr;
}
@GetMapping("/orders")
/* 비동기, 논블록킹 API호출 - CompletableFuture */
public CompletableFuture<UserOrder> asyncOrders2(String email) {
return asyncFindUser2(email)
.thenCompose(user -> ayncOrders2(user2))
.thenApply(orders -> new UserOrder(email, orders))
.exceptionally(e -> UserOrder.FAIL);
}
2개 이상의 비동기 작업을 병렬적으로 실행하고 결과를 모아서 결과 값을 만들어 내는 비동기 작업 구성
- ListenableFuture의 콜백 구조는 어렵다
- CompletableFuture로는 손쉽게 가능하다
1.1.1. Define “Reactive”
옵서버 패턴(observer pattern)은 객체의 상태 변화를 관찰하는 관찰자들, 즉 옵저버들의 목록을 객체에 등록하여 상태 변화가 있을 때마다 메서드 등을 통해 객체가 직접 목록의 각 옵저버에게 통지하도록 하는 디자인 패턴이다. 주로 분산 이벤트 핸들링 시스템을 구현하는 데 사용된다. 발행/구독 모델로 알려져 있기도 하다.
- Observer pattern 문제
초당 10개의 메모리만 처리할 수 있는 구독자(Subscriber)에게 발행자(Publisher)가 초당 100개의 메모리를 푸시한다면 최악의 경우 out of memory에러가 발생한다.
- back pressure
백 프레셔의 기본 원리는 발행자가 데이터를 전달할 때 구독자가 필요한 만큼만 전달한다.
1. Subscriber 가 subscribe 메소드를 통해 Publisher 에게 구독을 요청합니다.
2. Publisher는 onSubscribe 함수를 사용해 Subscriber에게 Subscription을 전달합니다.
3. 이제 Subscription은 Subscriber와 Publisher 간 통신의 매개체가 됩니다. Subscriber는 Publisher에게 직접 데이터 요청을 하지 않습니다. Subscription의 request 함수를 통해 Publisher에게 전달합니다.
4. Publisher는 Subscription을 통해 Subscriber의 onNext에 데이터를 전달하고, 작업이 완료되면 onComplete, 에러가 발생하면 onError 시그널을 전달합니다.
5. Subscriber와 Publisher, Subscription이 서로 유기적으로 연결되어 통신을 주고받으면서 subscribe부터 onComplete까지 연결되고, 이를 통해 백 프레셔가 완성됩니다.
1.1.2. Reactive API
1. Publisher
2. Subscriber
3. Subscription
4. Processor
public interface Publisher<T> {
public void subscribe(Subscriber<? super T> s);
}
public interface Subscriber<T> {
public void onSubscribe(Subscription s);
public void onNext(T t);
public void onError(Throwable t);
public void onComplete();
}
public interface Subscription {
public void request(long n);
public void cancel();
}
public interface Processor<T, R> extends Subscriber<T>, Publisher<R> {
}
1.1.3. Programming Models
- 2가지 개발방식 지원
- 기존의 @MVC 방식
- @Controller, @RestController, @RequestMapping
- 새로운 함수형 모델
- RouterFunction, HandlerFunction
Webflux 성능 저하의 원인
- log 메서드는 block I/O이기 때문에 성능 저하의 원인이 된다.
- map, flatMap 메서드
- map은 동기식 함수를 적용하여 Mono의 아이템을 변경하는데 사용하는 메서드이다.
- flatMap은 Async하게 Mono의 아이템을 변경하는데 사용하는 메서드이다.
다른 성능 개선 사항
- BlockHound
- Blocking 코드를 찾아주는 라이브러리
- reactor-core 3.3.0 부터 내장
- block(), blockFirst(), blockLast() 같은 메서드를 사용하면 예외 발생
- Avoiding Reactor Meltdown
- Event Loop의 Thread들이 Blocking API 때문에 Reactor 시스템이 Hang걸리는 현상
- Blocking API를 위한 별도의 Thread Pool로 격리시키는 방법
- subcribeOn() : 전체 메서드 체인을 별도의 스레드 Pool에서 실행함
- publishOn() : 메서드 체인 가운데 다음 메서드 체인을 별도의 Pool에서 실행함
Reference
'Spring' 카테고리의 다른 글
Spring Boot 에서 H2 innodb 오류 해결 (1) | 2020.05.22 |
---|---|
Spring Boot에서 JWT 사용하기 (0) | 2020.05.18 |
댓글