Spring Framwork5에서 새롭게 추가된 모듈이다. webflux는 reactive 스타일의 어플리케이션 개발을 도와주는 모듈이라한다. 즉 Reactive Programming방식의 스프링이다.
Reactive Programming(반응 형 프로그래밍)은비동기(Asynchronous)식 및 이벤트 기반(event-driven)의Non Blocking이다.
웹 플럭스는 기존 MVC처럼 이용할 수 있고, 기본으로 Netty를 제공한다고 함.
WebFlux는 아래와 같은 용도로 사용하는 것을 추천 한다고 합니다.
비동기 - 논블록킹 리액티브 개발에 사용
효율적으로 동작하는 고성능 웹어플리케이션 개발
서비스간 호출이 많은 마이크로서비스 아키텍처에 적합
기존 MVC VS 웹 플럭스
기존 MVC 방법 Spring MVC의 경우 어플리케이션이 실행되면서 Thread Pool을 만들어 놓는다. 요청이 들어오면 그 요청을 Queue에 쌓고 이것을 Thread Pool의 Thread들이 요청을 가져가 처리한다. 하지만 많은 요청이 들어와 Pool Size를 초과하여 Queue에 계속 요청이 쌓이는 경우가 발생할 수 있다.
웹 플럭스
웹 플럭스의 2가지 방식
1. 기존 에노테이션 방식
@Controller
...{
// 애너테이션 기반 라우팅
@GetMapping("/hello")
@ResponseBody
public Mono<String> getHello() {
return demoService.getHello();
}
}
2. 함수형 프로그래밍 방식
@Component
// 함수 기반 라우팅
@Bean
public RouterFunction<ServerResponse> routes(DemoHandler demoHandler) {
return RouterFunctions
.route(RequestPredicates.GET("/hello"), demoHandler::getHello);
}
두가지 중에 편한 방법을 통해서 사용해도 무방하다.
웹 플럭스의 반환형
아래와 같이MVC에서 일반적으로 사용하던 Plain Object를 사용할 수 없고 반드시 Publisher Object로 감싸서 반환해야 한다. 이 때 Publisher는 Reactive Stream Interface중 하나인데 (Processor, Publisher, Subscriber, Subscription) Reactive Stream Interface에 관한 설명은brunch.co.kr/@springboot/153참고하기 바란다.
2. Mono & Flux란?
Publisher의 실제 구현체 즉 웹 플럭스에서 반환하는 형식이라고 생각해도 무방.
Flux (0-N개의 데이터)
Flux는 Publisher의 구현체로서, 0-N개의 데이터를 발행(전달, 방출)할 수 있다. 하나의 데이터를 전달할때마다 onNex 이벤트를 발생시키고, 모든 데이터 전달이 완료되면 onComplete 이벤트가 발생하며 오류가 발생할 경우 onError이벤트가 발생한다.
이러한 주의사항이 있는 이유는 spring AOP가 프록시 패턴의 개념을 이용하기 때문이다. 즉 프록시 클래스에서 @Async 메소드를 호출해야 하는데 private하거나, self로 호출하면 프록시클래스에 의해 호출되지 않기 때문이다.
※프록시 패턴글에 자세한 설명 있음
2. completablefuture란?
@Async같은 비동기 메소드를 사용할 때, Void형태를 사용한다면 문제가 되지 않는다. 하지만 return이 존재한다면 기존에는 Future나 ListenableFuture를 이용하여 해결했지만 JAVA 8버전 부터는 CompletableFuture를 제공하므로 이를 사용하는 방법을 정리한다.
다음은 Completeablefuture를 사용한 예제이다.
다음과 같이 @Async 메소드에서는 결과 값을 CompletableFuture객체에 담아서 반환해야하고 호출한 곳에서는 이 CompletableFuture 객체를 이용해야한다.
@Service
public class GitHubLookupService {
@Async
public CompletableFuture findUser(String user) throws InterruptedException {
//로직
return CompletableFuture.completedFuture(results);
}
}
return을 CompletableFuture를 통해서 하는것을 알았으니 return 받은 쪽 에서는 어떻게 사용하는지 확인해보자.
get 함수란?
다음과 같이 .get()메소드를 통해 결과를 확인할 수 있다.
get을 하게 되면 page1의 수행이 끝날때까지 잠시 기다렸다가 결과값을 반환하게 된다.
public void run(String... args) throws Exception {
//호출 해서 저장
CompletableFuture page1 = gitHubLookupService.findUser("PivotalSoftware");
//다른 로직
System.out.println(page1.get());
}
그림으로 표현해보면 다음과 같다.
thenCompose 란?
하나의 CompletableFuture가 수행하고 나온 결과를 넘겨주어 다음 CompletableFuture객체가 이를 가지고 다른 메소드를 수행하는 방법.
publicvoidrun(String... args)throws Exception {
//호출 해서 저장
CompletableFuture<Integer> price1 = testService.getPrice(100);
//다른 로직
price1.thenCompose(result -> testService.getPrice(result)).join();
}
그림으로 표현하면 다음과 같다.
thenCombine 이란?
CompletableFuture를 2개 병렬 실행해서 결과를 조합할 때 사용한다. thenCompose와 달리 두개를 동시에 실행한다는 차이가 있다.
publicvoidrun(String... args)throws Exception {
//호출 해서 저장
CompletableFuture<Integer> price1 = testService.getPrice();
CompletableFuture<Integer> price2 = testService.getPrice();
//다른 로직
price1.thenCombine(price2, (a, b) -> a+b);
}
allof 란?
thenCombine처럼 병렬 실행해서 결과를 조합할 때 사용한다. CompletableFuture를 3개이상 조합할 때 사용한다.
따라서 빌더패턴을 적용해보면 다음과 같다. 아래와 같이 set을 통해 변수값을 지정하고 build를 통해 객체를 생성하는 방식이다. 사용자는 set을 통해 파라미터값을 정하기 때문에 생성자 방식처럼 순서에 맞게 작성하지 않아도 된다는 장점이 있고 예시는 클래스를 따로 나눴지만 내부 클래스를 이용해 하나의 클래스처럼 이용할 수 있다.
퍼사드 패턴을 이용하지 않았다면 Movie를 볼때의 과정을 전부 직접 호출했어야하고, 다른 과정 음악듣기 같은경우 Movie와 다른 과정으로 직접 실행해야한다 하지만 퍼사드 패턴을 이용하면 다음과 같이 실행할 수 있다.
HomeTheaterFacade homeTheater =
new HomeTheaterFacade(amp, tuner, dvd, cd,
projector, screen, lights, popper);
homeTheater.watchMovie("Raiders of the Lost Ark");
팩토리 메소드 패턴에서는 서브 클래스에서 어떤 클래스를 만들지 결정하게함으로서 (즉 추상클래스를 상속받은 NYPizzaStore에서 추상메소드를 수행함) 객체 생성을 캡슐화 한다.
이러한 팩토리 메소드 패턴에서 찾아볼 수 있는 객체 지향적 디자인 원칙은 의존성 뒤집기 이다.
기존 PizzaStore는 각 피자가 바뀔때마다 수정되어야하는 방식의 의존성을 가지고 있었으나.
PizzaStore-> NYStyleCheesePizza
PizzaStore-> ChicagoStypeCheesePizza
PizzaStore-> NYStyleVeggiePizza
피자 인터페이스를 사용하여 인터페이스에만 의존적이고 나머지 실제 구현 객체는 인터페이스에 의존적인 관계가 되어 의존성 뒤집기 원칙을 가지고 있다.
PizzaStore->Pizza
Pizza<- NYStyleCheesePizza
Pizza<- ChicagoStyleCheesePizza
Pizza<- NYStyleVeggiePizza
2. 추상 팩토리 패턴
피자를 만들기 위한 원재료를 처리하는 방법에 대해 고민해보자.
지역별 공장들을 추상화한 인터페이스를 가지는 구조로 작성한다
PizzaIngredientFactory 인터페이스
publicinterfacePizzaIngredientFactory{
public Dough createDough();
public Sauce createSauce();
public Cheese createCheese();
public Veggies[] createVeggies();
public Pepperoni createPepperoni();
public Clams createClams();
}