4-2. 리액터 프로젝트 심화학습
리액티브 스트림의 수명 주기
조립(assembling) 단계
- 처리 흐름에서 사용하는 연산자를 조합한 빌더 API처럼 보이지만 일반적인 빌더 패턴과 달리 리액터 API는 불변성(Immutability)을 제공한다.(적용된 각각의 연산자가 새로운 객체를 생성한다.)
- 스트림 구성을 조작하고 더나은 스트림 전달을 위한 다양한 기술을 적용할 수 있는 단계
구독 단계
- 특정 Publisher를 구독할 때 발생
- 조립 단계에서 일련의 Publisher 체인이 연결되었고 최상위 래퍼를 구독하면 해당 체인에 대한 구독 프로세스가 시작된다.
- 조립단계와 동일한 최적화를 수행할 수 있다.
- 리액터에서 멀티 스레딩을 지원하는 일부 연산자는 구독이 발생하는 작업자를 변경할 수 있다.
런타임 단계
- 게시자와 구독자 간에 실제 신호가 교환되는 단계
- 교환하는 처음 두 신호는
onSubscribe
,request
- onSubscribe 메서드는 최상위 소스에서 호출
- 구독이 모든 구독자 체인을 통과하여 마지막 구독자가 구독 체인에 대한 정보를 수신하고 메시지 수신을 시작하려면 Subscription#request 메서드를 호출해 전송을 시작해야 한다.
- 런타임 중에도 request를 줄이기 위한 최적화를 적용할 수 있다.
리액터에서 스레드 스케줄링 모델
멀티스레딩 실행을 위해 제공하는 연산자 사이의 차이점에 대해서 확인
다른 워커로 실행을 전환할 수 있는 네 가지 연산자
publishOn 연산자
- 런타임 실행의 일부를 지정된 워커로 이동
- Scheduler 인터페이스를 사용하여 현재 스트림에 대한 특정 워커를 선택할 수 있다.
- 내부적으로 전용 워커가 메시지를 하나씩 처리할 수 있도록 새로운 원소를 제공하는 큐를 가지고 있다.
- 리액티브 스트림의 모든 원소는 하나씩(동시에는 아니지만) 처리되므로 항상 모든 이벤트에 순서를 엄격하게 정의할 수 있다.(이 속성을 **직렬성(serializability)**라고 한다.)
- 병렬 처리를 할 수 없다는 말처럼 들리지만 병렬 처리도 가능한데 예를 들어 처리 단계 사이에 비동기 영역을 추가해서 독립적으로 작업해 비동기 처리를 할 수 있다.
subscribeOn 연산자
- 구독체인에서 워커의 작업 위치를 변경
- 보통 호출 시점에서 상위 스트림에 해당하는 부분의 스레드를 설정
parallel 연산자
- 하위 스트림에 대한 플로 분할과 분할된 플로 간 균형 조정 역할
1 | Flux.range(0, 10000) |
- parallel연산자를 사용하면 ParallelFlux를 동작시킨다.
- 다수의 Flux를 추상화하여 Flux간에 데이터의 크기 균형을 이룬다.
Scheduler
Scheduler.schedule
: Runnable 작업을 예약가능Scheduler.createWorker
: 동일한 방법으로 Runnable 작업 예약이 가능한 Worker 인터페이스의 인스턴스를 제공- Scheduler인터페이스 / Workder인터페이스의 차이점 : 워커 풀 / Thread 또는 리소스를 추상화한 것
- 리액터에서 제공하는 스케줄러 인터페이스의 3가지 주요 구현체
SingleScheduler
: 모든 작업을 한 개의 전용 워커에 예약가능, 시간에 의존적ParallelScheduler
: 고정된 크기의 작업자 풀에서 작동(CPU 코어 수로 기본크기 제한)ElasticScheduler
: 동적으로 작업자를 만들고 스레드 풀을 캐시, 생성된 스레드 풀의 최대 개수는 제한되지 않음
리액터 컨텍스트
- Context는 스트림을 따라 전달되는 인터페이스
- 런타임 단계에서 필요한 컨텍스트 정보에 엑세스할 수 있도록 하는 것
- 멀티스레드 환경의 비동기 처리방식에서 ThreadLocal가 가지는 한계를 해결할 수 있다.
- 변수에 데이터를 넣은 후 publishOn 등을 통해 다른 워커에서 작업 플로를 수행하면 데이터를 쌓은 스레드와 작업 스레드가 달라서 데이터에 접근할 수 없다.