티스토리 뷰
스프링프레임워크 개발자이자 스프링 부트를 만든 Phil Webb 이란 분의 SpringOne Platform 2017 동영상인데 19분 정도에 reactive stack 과 servlet stack을 왜 선택해야 하느냐? 라는 질문의 답이 있습니다.
우선 영상을 보시면 이해가 쉬울 것 같습니다.
블럭킹과 서블릿에 대한 이해는 스레드를 수영장의 레인으로 생각해서 각각의 레인은 외부 요청을 처리하기 위한 프로세스나 스레드의 시작과 끝이라고 가정합니다.
일반적인 블럭킹 방식에서는 느린 요청이 오면 어플리케이션 성능 저하가 일어나고 해당 부분을 제거하기 위해서 스레드를 추가하는 방법을 사용하는데 이 부분은 자원 사용으로 인해 비용 문제가 발생합니다.
논블럭킹 방식에서는 요청 후에 해당 스레드가 대기하지 않기 때문에 더 효율적입니다.
Project Reactor 문서의 리액티브 프로그래밍 소개
현대 어플리케이션에서 성능은 주요한 관심사이다.
성능의 향상을 위해서 더 많은 쓰레드와 하드웨어 자원을 사용해서 병렬화하거나 자원을 좀 더 효율화하는 방법을 찾는다.
대개 자바 개발자들이 블럭킹 코드를 사용하고 병목이 생기면 쓰레드를 늘린다.
블로킹 코드는 데이터베이스 요청이나 네트워크를 통한 호출 시 데이터를 기다리는 동안 유휴상태로 있기 때문에 낭비이다.
병렬화는 묘책, 특효약(silver bullet)이 아니다.
하드웨어를 최대로 사용하기 위해서 필요하지만 자원 낭비와 복잡해지는 문제가 있다.
자원 효율화를 위해서 비동기 프로그래밍을 한다.
주로 자바에서는 Callbacks과 Futures를 사용한다.
Example Of Callback Hell
userService.getFavorites(userId, new Callback>() {
public void onSuccess(List list) {
if (list.isEmpty()) {
suggestionService.getSuggestions(new Callback>() {
public void onSuccess(List list) {
UiUtils.submitOnUiThread(() -> {
list.stream()
.limit(5)
.forEach(uiList::show);
});
}
public void onError(Throwable error) {
UiUtils.errorPopup(error);
}
});
} else {
list.stream()
.limit(5)
.forEach(favId -> favoriteService.getDetails(favId,
new Callback() {
public void onSuccess(Favorite details) {
UiUtils.submitOnUiThread(() -> uiList.show(details));
}
public void onError(Throwable error) {
UiUtils.errorPopup(error);
}
}
));
}
}
public void onError(Throwable error) {
UiUtils.errorPopup(error);
}
});
Callback Code와 같은 Reactor Code
userService.getFavorites(userId)
.flatMap(favoriteService::getDetails)
.switchIfEmpty(suggestionService.getSuggestions())
.take(5)
.publishOn(UiUtils.uiThreadScheduler())
.subscribe(uiList::show, UiUtils::errorPopup);
Timeout과 Fallback이 있는 Reactor Code
userService.getFavorites(userId)
.timeout(Duration.ofMillis(800))
.onErrorResume(cacheService.cachedFavoritesFor(userId))
.flatMap(favoriteService::getDetails)
.switchIfEmpty(suggestionService.getSuggestions())
.take(5)
.publishOn(UiUtils.uiThreadScheduler())
.subscribe(uiList::show, UiUtils::errorPopup);
참고
Reactive programming vs. Reactive systems
https://www.reactivemanifesto.org/ko
https://dzone.com/articles/reactive-programming-with-spring-5
https://projectreactor.io/docs/core/release/reference/#_blocking_can_be_wasteful
spring blog
https://spring.io/blog/2016/02/09/reactive-spring
https://spring.io/blog/2016/04/19/understanding-reactive-types
https://spring.io/blog/2016/06/07/notes-on-reactive-programming-part-i-the-reactive-landscape
https://spring.io/blog/2016/06/13/notes-on-reactive-programming-part-ii-writing-some-code
https://spring.io/blog/2016/07/28/reactive-programming-with-spring-5-0-m1