제목: 실전! 스프링5를 활용한 리액티브 프로그래밍
- RX(Reactive Extensions): 동기/비동기 스트림에 관계 없이 명령형 언어를 이용해 데이트 스트림을 조작하는 도구
- 관찰자, 반복자, 함수형 프로그래밍의 조합으로 이루어져 있다
- 구독자가 관찰 가능한 스트림에 가입한 후 비동기적으로 이벤트를 생성해 프로세스를 시작하는 것이 핵심
- 연산자의 기능적 연결을 이용해 복잡한 비동기 데이터 흐름을 숨기는 것이 목표
- 리액티브 프로그래밍의 목적
- 응답성을 얻기 위함
- 응답성: 평균 지연 시간과 밀접한 연관이 있는 특성 → 분산 시스템의 비즈니스 핵심 가치
- 응답성은 메시지 기반의 시스템을 이용하여 탄력성, 복원력을 통해 얻을 수 있다
- 탄력성: 다양한 작업 부하에서 응답성을 유지하는 능력
- 복원력: 시스템 실패에도 자동으로 재시도함으로써 원치 않는 장애로 부터 보호하는 능력
- 옵저버 패턴, 스프링 EventListener과의 차이
- 옵저버 패턴: 한 개의 느린 리스너로 인해 전체가 느려지는 효과가 발생할 수 있다
- EventListener: 발행-구독 패턴의 구현
- 옵저버 패턴과는 달리 게시자와 구독자 사이에 채널 계층을 구현하여 서로를 알 수 없게 했다
- 이벤트 생명 주기를 위해 고려되었으며 고부하 및 고성능을 위한 기능은 아니다
- 리액티브는 왜 배압(흐름 제어)를 위해 하이브리드 푸시 모델을 선택했는가?
- 풀 방식은 구독자가 요청 할 때 마다 추가 시간(요청)이 소요된다
- 푸시 방식은 배치 처리를 통해 요청 횟수를 최소화 하여 전체 시간 최적화가 가능하다
- 하이브리드 푸시 방식은 배압을 직접 조절하거나 자동적으로 선택하도록 조정이 가능하다
- Subscriber의 onNext 함수에서 1이면 풀 방식. Long.Max이면 푸시 방식으로 동작시킬 수 있다
- 빠른 프로듀서와 느린 컨슈머의 문제
- 반대 경우인 느린 프로듀서와 빠른 컨슈머 문제는 프로듀서를 늘리는 것 만으로 해결이 가능하다
- 해결 방법 → 일반적으로 처리되지 않은 원소를 큐에 수집
- 무제한 큐: 메모리 한도까지 메시지를 무제한 저장
- 크기 제한 큐: 메시지의 중요성이 낮을 때 특정 알고리즘에 따라 이벤트를 드롭하는 큐
- 크기 제한 블록킹 큐: 제한에 도달하면 이벤트 유입을 차단하는 큐
- 최악의 해결책…시스템 전체 처리량을 줄여버린다
- 스프링 리액터
- 리액터 패턴
- 모든 이벤트는 큐에 추가되고 나중에 별도의 스레드에 의해 처리되는 패턴.
- 이벤트는 모든 관련 컴포넌트 핸들러로 발송되고 동기적으로 처리된다
- 프로세스의 결과는 처리 순서가 다르더라도 발생 순서를 보장한다
- 너무 정확한 시간 처리를 요구해서는 안된다 → 지연이 일부 존재한다
- Processor 인터페이스는 왠만하면 사용하지 말자
- 매크로, 마이크로 퓨전을 통해 연산자를 최적화하는 기능을 탑재하고 있다
- CPU를 많이 사용하는 작업은 별도 스레드(풀)를 이용해야 한다
- 배압 다루기
- onBackPressureXXX 함수를 이용하여 푸시 방식, 드롭 방식, 최신 유지 방식, 에러 생성 방식을 지정 할 수 있다
- 콜드 vs 핫 스트림
- 콜드: 구독자가 나타날 때 마다 모든 시퀀스의 데이터가 새로 생산되는 방식
- 핫: 미리 데이터를 생성하고 구독자가 생길 때 모아둔 데이터나 최신 데이터만을 생산하는 방식
- just 함수는 hot 퍼블리셔를 생성하므로 주의해서 써야한다
- 스케줄링 모델
- Worker 인터페이스: 스레드 또는 리소스를 추상화한 의미
- Scheduler 인터페이스: 워커 풀(집합)을 의미
- publishOn 연산자
- 런타임 실행의 함수 일부를 지정된 워커(스레드)로 이동 할 수 있게 해준다
- 즉, 함수 실행 이후의 특정 다운 스트림을 별도 워커에서 실행하도록 변경한다
- subscribeOn 연산자
- 구독 체인에서 워커의 작업 위치를 변경 할 수 있다
- 즉, 함수 실행 이전의 전체 업 스트림을 별도 워커에서 실행하도록 변경한다
- 컨텍스트
- 스트림을 따라 전달되는 인터페이스. 런타임 단계에서 스트림을 따라 전달됨
- 사용 팁
- defer 함수는 구독하는 순간에 실행되는 함수로 구독자 별로 서로 다른 데이터 생성이 가능하다
- subscriber를 확장해야 하는 경우 BaseSubscriber를 상속하는 것이 훨씬 좋은 방법이다
- scan 함수는 중간 결과(acc)를 다운스트림으로 보내 처리가 가능하도록 지원한다
- buffer 함수는 여러 개의 이벤트를 특정 숫자로 묶는 이벤트 컬렉션 생성을 지원한다
- doOnXXX 함수를 통해 각 시그널이 발생할 시 동작을 정의할 수 있다
- using 함수를 통해 try-with-resources 스타일을 적용할 수 있다
- transform 연산자를 통해 공통 부분만 별도로 추출하여 재 사용 가능하다
- 스프링 부트 2.X와 리액티브
- 스프링 부트의 핵심 모듈
- spring-boot: Ioc 컨테이너와 관련된 모든 가능한 기본 구성이 제공되는 모듈
- spring-boot-autoconfigure: 기존 스프링 프로젝트에 필요한 설정을 제공하며 특정 의존성이 추가되기 전까지는 비활성화 되어 있음
- 스프링 5.x에서 리액티브 관련 기능이 추가되었으며, 이 중 web-flux 모듈은 내장된 배압 기능을 제공
- 모니터링 및 추적을 위해 스프링 클라우드 슬루스 ****모듈이 리액티브를 지원하도록 변경
- 스프링 Web-flux
- MVC(동기 블록킹)와는 달리 비동기 논 블록킹 웹 모듈
- MVC의 아키텍처를 따르므로 변경이 용이
- RestTemplate를 대체하는 논 블록킹 Web-Client의 도입
- WebSocket 및 SSE(Server Sent Event)는 동일하게 지원
- I/O 상호작용이 많을 때 매우 효과적
- 스프링 리액티브 데이터
- 스프링 데이터 모듈 중 리액티브 방식으로 데이터를 접근하는 모듈
- 마이크로 서비스에서의 데이터 및 트랜잭션
- 적절하게 설계된 마이크로 서비스는 모든 서비스가 상태 의존적이지 않으며, 상태 정보는 데이터 지속성 관리하는 특수 서비스(ex: DBMS)에 저장되어야 한다
- 분산 트랜잭션: 2 PC를 이용하여 여러 서비스가 동시에 커밋에 동의하는 트랜잭션 → MSA에서는 권장되지 않음
- 이벤트 기반: 이벤트 브로커를 이용하여 이벤트 기반 트랜잭션을 이용하고 결과적 일관성을 달성하는 방식
- 이벤트 소싱: 비즈니스 엔티티의 상태 변화 순서를 저장하는 방식으로 이를 재현하여 현재 상태를 구성
- CQRS: 이벤트 소싱과 함께 종종 사용되며, 읽기/쓰기 저장소를 분리하여 이벤트 소싱의 단점을 해결
- CRDT: 충돌 없는 복제 데이터 타입
- 데이터 충돌이 발생하여 병합이 필요할 때 항상 성공하는 데이터 타입
- 여러 번 계산에 걸쳐 복제되고 조정 없이 동시에 업데이트된다
- 병합되더라도 일관된 상태를 유지한다
- ex: Grow-only Counter/Set, 2-Phase Set, Last-Write-Win Set
- 레디스는 기본적으로 이를 지원하고 있다
- 와이어 프로토콜: 일반적으로 TCP/IP를 이용하여 데이터베이스와 통신하는 프로토콜
- 트랜잭션
- 쿼리가 끝나기 전에 첫 번째 결과를 사용할 수 있고, 낮은 메모리/커넥션/스레드 사용량을 자랑한다
- ReactiveCrudRepository는 기존 CrudRepository와 달리 페이징/ 트랜잭션을 지원하지 않는다
- 동기식 Repository를 스레드 풀로 랩핑하는 경우 JPA 사용을 주의해야 한다 → 지연 로딩에서 프록시 문제가 발생 할 수 있음
- 스프링 클라우드 스트림
- RSocket
- 리액티브 스트림 테스트
- 릴리즈 + 스프링 액츄에이터
- 자바에서는 보편적으로 명령형 프로그래밍 기법을 이용한다
- 자바 스레드는 64비트 JVM에서 스택 크기 1024 KB를 사용하는 오버헤드가 존재한다
- 관찰자(Observer) 패턴은 약간의 수정으로 리액티브 프로그래밍의 기초가 된다
- 자바8의 CompletionStage, CompletionFuture 은 컨텍스트 스위칭과 메모리 오버헤드로 인해 효율성이 떨어진다
- 자바의 스트림 API는 풀링 방식으로 Iterator 를 개량하여 사용한다 → SplitableIterator
- 현재 JPA는 리액티브 지원을 할 예정이 없다? → https://github.com/hibernate/hibernate-reactive