React 프로그래밍이란?
모든것을 Stream으로 보고, 모든 데이터의 흐름을 시간 순서에 의해 전달되어지는 스트림으로 처리
하나의 문제를 비동기와 논블로킹 방식으로 실행될 수 있는 여러 단계로 분리할 수 있으며, 무한한 입력이나 입.출력을 생성할 수 있는 작업흐름(workflow)을 만들기 위해 결함
비동기는 클라이언트에서 서비스로 전송이 요청온 이후 임의의 시점에 처리된다는 의미이며, 논블로킹을 가능하게 하는 리액티브 프로그래밍에서 굉장히 중요한 기술
필요한 경우에만 스레드를 생성 후 메시지 형태로 전달하기 때문에 효율적으로 컴퓨터 리소스를 사용할 수 있다.
React 프로그래밍 원칙
즉각 반응, 반응성(Responsive)
- 지속적으로 긍정적인 사용자 환경을 보장하기 위해서, 모든 사용자에게 신속하게 반응
- 응답성을 촉진하기 위해서는 탄력성과 복원성이 있어야하며, 이를 성취하기 위해서는 메시지 구동 방식의 시스템이어야 함
탄력성(Resilient)
- 일반적인 상황뿐만 아니라 장애 등의 상황에서도 응답성을 보장ㅇ받기 위해 적절한 설계 및 아키텍처 원칙을 적용
- 탄력성은 실패시의 대응성에 대한 것이며, 이상적이지 않은 상황에서 반응성을 보장
유연성(Elastic)
- 응답성이 뛰어난 어플리케이션을 지속적으로 생성할 때 탄력성과 유연성이 함께 작용하며, 부하의 상황에서도 응답성을 보장
- 메시지 중심(Message-driven)
ㄴ 메시지 기반 아키텍처는 Reactive Application의 기반
ㄴ 메시지 기반 어플리케이션은 이벤트 기반, 엑터 기반 또는 이 둘의 조합일 수 있다.
React Application 개발시 주의사항
전체 stack이 reactive해야 한다
중간에 어느 한 stack이라도 reactive하지 않다면 전체 application은 reactive하게 동작하지 못하게 됨(어느 한 곳에 blocking 될 수 있는 가능성이 생기기 때문)
그렇기 때문에 Spring Boot는 Netty를 default로 채택하고 있음
Spring webflux dependency를 추가했다면, Spring mvc dependency는 함께 추가하면 안되며, 두 dependency를 함께 추가하면 spring boot는 동작하지 않음
React API
Publisher는 Subscriber의 요청에 따라 제한 없는 일련의 데이터 제공
Publisher를 이용해서 스트림을 정의하고 Subscriber를 이용해서 발생한 신호를 처리
Subscriber가 Publisher로부터 신호를 받는것을 Subscribe(구독)이라고 한다
둘의 관계는 publisher.subscribe(Subscriber)로 연결 > 시작
데이터 / 이벤트 / 신호(signal)이라는 용어를 사용한다.
- React 스트림은 onNext | onComplete | onError 세 신호를 발생할 수 있다.
- onComplete 와 onError는 둘 중 하나만 발생할 수 있으며, 발생하지 않을 수도 있다.
용어 및 주요 사항 정리
Mono, Flux
- 데이터(시퀀스)를 제공하는 발생자 역할을 하는 Publisher의 구현체
Flux
Publisher<T> 인터페이스의 구현체로 하나의 Flux로부터 다른 Flux혹은 Mono를 생성할 수 있도록 API를 제공
0 ~ n 개의데이터를 방출(emit)할 수 있으며, 하나의 데이터를 방출할 때마다 onNext 이벤트가 발생하고, doOnNext로 등록된 @FunctionalInterface Consumer<T>가 호출
Flux내의 모든 데이터에 대한 처리가 완료되면 onComplete이벤트가, 오류가 발생하면 onError 이벤트가 발생되며, 각각 등록된 함수형 인터페이스 구현체과 호출된다
종료 이벤트가 발생하지 않는 경우 이 Flux는 무한히 데이터를 방출한다
새로운 Mono를 생성하는 메소드가 있다.
오퍼레이터(operator) 즉, 인스턴스 메소드를 이용하면 비동기적인 처리 순서를 생성하는 비동기 처리 파이프라인을 구성할 수 있다.
Flux.subscribe(), Flux.publish, Flux.publishNext와 같은 멀티캐스트 오퍼레이터를 이용하면 처리 파이프라인에 대한 객체를 생성하고 그 안에서 데이터가 흘러가도록 만든다.
Mono
Flux와 마찬가지로 Publisher<T>의 구현체
0개 혹은 1개의 데이터만 방출
데이터를 방출하지 않을 때는 Mono<Void>를 사용하면 되고, 처리가 종료 여부만 중요한 경우 사용
몇몇 메소드들은 새로운 Flux를 생성하여 리턴해주기도 하며, 이는 데이터 흐름 속에서 처리되는 개체의 수가 변경될 수 있음을 의미한다.
Push vs Pull
Subscription#request()는 Subscriber가 데이터를 처리할 수 있을 때 Publisher에게 데이터를 요청하는 풀(pull) 모델이다.
하지만 request(Long.MAX_VALUE)로 요청하면 Publisher는 개수 제한 없이 Subscriber에 데이터를 전송한다.
이는 완전한 푸시(push) 모델이다.
또 request(100000)을 사용하면 십 만 개의 데이터를 요청하고, Publisher는 발생한 데이터가 십 만 개가 될 때까지 신호를 보낸다.
데이터 요청은 풀 모델로 이루어졌지만 10만 개의 데이터를 전송하는 동안은 실질적으로 푸시 모델과 같다.
StepVerifier
선언적 프로그래밍(Declarative Programming)의 단점으로 테스트하기가 어려우나, 이를 보안하기 위해 StepVerifier라는 Flux/Mono 테스트 도구를 제공
모든 Publisher<T> 구현체의 처리단계를 확인할 수 있다.
Transform
생성된 Flux/Mono는 다양한 방식으로 다른 형태, 다른 데이터로 변형될 수 있다
웹서비스 호출에는 대기시간(latency)가 발생할 수밖에 없고, 그래서 동기화된 map 메소드를 사용할 수 없다.
대신 Flux나 Mono와 같은 비동기 호출(asynchronous call)을 이용해야 하는데 이 때 사용하는 오퍼레이터가 flatMap
Merge
여러 개의 publishier로부터 전달되는 데이터의 흐름을 하나의 Flux로 합치는 작업으로 mergeWith 메소드를 사용한다.
mergeWith 메소드를 이용하는 경우, 먼저 도착하는 데이터가 먼저 처리되도록 Flux가 생성되며, 병합되는 Flux들의 순서가 보장되지 않고, 늦게 도착하는 데이터는 끼워넣어지게(interleave) 된다.
만약 병합되는 Flux들의 순서를 지키고 싶다면, mergeWith 대신 concat 메소드를 사용해야 함
Request
배압(backpressure)
Subsciber가 Publisher로 데이터 방출 속도를 제한하는 신호(signal)을 보내는 피드백 메커니즘
제어는 Subscription 단계에서 수행됨
매 subscribe() 메소드 호출을 위해 하나의 Subscription 객체가 ㅅ애성되는데, 데이터 흐름을 최소하기 위해 cancel() 메소드를 호출할 수도 있고 데이터의 양을 조절하기 위해 request(long) 메소드를 호출할 수도 있다.
request(Long.MAX_VALUE)는 제한 없이 전송할 것을 요구하는 것이며, 이럴 경우 Publisher는 가능한한 빠른 속도로 데이터를 방출한다
StepVerifier를 생성하는 방법
- create(Publisher<T>), withVirtualTime(Supplier<? extends Publisher<? extends T>>)
StepVerifier가 생성된후 thenRequest(long) 메소드를 이용하여 배압을 설정할 수 있으며, 설정된 값 만큼만 데이터가 방출된다.
Reactive to blocking
Mono > block(), Flux > toIterable() 등의 메소드를 이용할 수 있다.
이 메소드들은 처리과정에서 onError 이벤트를 발생된 경우 해당 Exception을 던지게 된다.
해당 내용을 사용할 경우 전체 리액티브 파이프라인이 잠재적으로 락이 걸릴수 있다.
Blocking to Reactive
Scheduler를 통해 블록킹 코드가 자신만의 실행 맥락(execution context) 내에서 실행되도록 고립하고, 나머지 파이프라인은 고효율을 유지하도록 하면서 필요한 만큼만의 thread를 생성하는게 가장 좋은 방식이다
subscribeOn 메소드를 이용하면 시작부터 매개변수로 전달된 Scheduler에서 시퀀스를 고립할 수 있다.
Scheduler.elastic() 메소드는 요구되는 크기 만큼의 쓰레드풀을 생성하고 사용되지 않게 되면 자동으로 해당 쓰레드들을 해제한다.
느린 subsciber를 위해서는 작은 부분만을 고립하기 위해 publishOn 오퍼레이터를 이용한다.
'JAVA > Reactor' 카테고리의 다른 글
비동기 멀티 스레드 생성 (0) | 2020.06.08 |
---|---|
프로그래밍 방식의 시퀀스 생성 (0) | 2020.06.04 |
배압과 요청 재구성 방법 (0) | 2020.06.04 |
람다의 대안 : BaseSubscriber (0) | 2020.06.04 |
Flux, Mono 생성 후 Subscribe (0) | 2020.06.04 |