반응형

Spring WebFlux는 Reactor 프로젝트를 기반으로 비동기·논블로킹 애플리케이션을 구축합니다. 이 환경에서 Scheduler는 스레드 관리를 담당하며, 병렬 처리와 리소스 최적화의 핵심 요소입니다.
1. Scheduler 기본 개념
1.1 물리 스레드 vs 논리 스레드
| 구분 | 설명 |
|---|---|
| 물리 스레드 | CPU 코어 수에 해당, 하드웨어적으로 생성 (예: 8코어 CPU → 8물리 스레드) |
| 논리 스레드 | 소프트웨어적으로 생성·관리, 동시성 구현을 위해 사용 |
1.2 Scheduler의 역할
- 스레드 풀 관리: 작업을 적절한 스레드에 할당
- 실행 컨텍스트 제어:
subscribeOn,publishOn과 조합해 실행 흐름 최적화 - 리소스 제한: 스레드 개수, 대기 큐 크기 등을 제어해 시스템 과부하 방지
2. 주요 Scheduler 종류
2.1 기본 제공 Scheduler
| Scheduler | 특징 | 사용 시나리오 |
|---|---|---|
immediate() |
현재 스레드에서 즉시 실행 | 테스트, 간단한 동기 작업 |
single() |
단일 스레드 재사용 | 짧은 지연 작업 |
boundedElastic() |
Blocking I/O 최적화 (기본: CPU 코어×10, 최대 100,000 작업 큐) | DB 호출, 파일 I/O |
parallel() |
Non-Blocking CPU 작업 최적화 (CPU 코어 수만큼 스레드 생성) | 계산 집약적 작업 |
newXXX() |
커스텀 스레드 풀 생성 (이름, 데몬 스레드 옵션 등) | 특수 요구사항이 있는 경우 |
2.2 Scheduler 선택 가이드
- Blocking 작업:
boundedElastic()(예: JDBC, 레거시 시스템 통합) - Non-Blocking 작업:
parallel()(예: Reactive MongoDB, WebClient) - 단순 작업:
single()또는immediate()
3. 핵심 Operator와의 조합
3.1 subscribeOn()
- 특징: 구독 시점의 스레드를 지정 (전체 체인 영향)
- 사용법: 체인 어디에 위치해도 동일하게 동작
Mono.fromCallable(() -> blockingApiCall()) .subscribeOn(Schedulers.boundedElastic()) // Blocking 작업에 필수 .subscribe();
3.2 publishOn()
- 특징: 하위 체인의 스레드만 변경 (위치에 따라 영향 범위 달라짐)
Flux.range(1, 10) .publishOn(Schedulers.parallel()) // 이후 연산은 parallel 스레드 .map(i -> i * 2) // parallel 스레드에서 실행 .publishOn(Schedulers.single()) // 이후 연산은 single 스레드 .subscribe(); // single 스레드에서 실행
3.3 parallel() + runOn()
- 병렬 처리 전용 조합
Flux.range(1, 100) .parallel(4) // 4개 물리 스레드 분할 .runOn(Schedulers.parallel()) // 각 분할 작업을 parallel 스레드에서 실행 .map(i -> i * 2) .sequential() .subscribe();
4. 실전 활용 예제
4.1 Blocking API 호출
public Mono fetchUser(String id) {
return Mono.fromCallable(() -> legacyBlockingApi.getUser(id))
.subscribeOn(Schedulers.boundedElastic());
}
4.2 Non-Blocking 병렬 처리
Flux.range(1, 1000)
.parallel()
.runOn(Schedulers.parallel())
.map(i -> intensiveCalculation(i))
.sequential()
.collectList()
.subscribe();
4.3 Hybrid 처리 (Blocking + Non-Blocking)
webClient.get()
.uri("/api/data")
.retrieve()
.bodyToMono(Data.class)
.publishOn(Schedulers.parallel()) // Non-Blocking 처리
.flatMap(data -> processData(data))
.publishOn(Schedulers.boundedElastic()) // Blocking 저장 작업
.flatMap(result -> saveToLegacyDB(result))
.subscribe();
5. 주의사항 및 모범 사례
- Blocking 작업 반드시 분리:
boundedElastic()없이 Blocking 호출 시 성능 저하 - Operator 위치 신중히 결정:
publishOn은 하위 체인만 영향 - 스레드 풀 남용 금지:
newXXX()남발 시 메모리 누수 가능성 - 리소스 정리:
Scheduler인스턴스는 사용 후dispose()호출 - 테스트 시 가상 시간 활용:
StepVerifier.withVirtualTime()
결론
WebFlux에서 Scheduler를 효과적으로 활용하려면:
- 작업 유형(Blocking/Non-Blocking) 식별
- 적합한 Scheduler 선택 (
boundedElasticvsparallel) subscribeOn과publishOn의 실행 범위 이해- 병렬 처리 시
parallel()+runOn()조합 사용
이를 통해 시스템 리소스를 효율적으로 관리하면서 고성능 비동기 애플리케이션을 구축할 수 있습니다.
[WebFlux] Scheduler
Spring WebFlux는 비동기 및 논블로킹 애플리케이션을 구축하기 위한 프레임워크로, Reactor 패러다임...
blog.naver.com
[WebFlux] 리액티브 스트림즈 Sinks: 핵심 개념과 활용 가이드
Reactor의 Sinks는 리액티브 스트림즈에서 프로그래밍 방식으로 Signal을 전송하는 표준 메커니즘입니다. 기존 Processor의 한계를 극복하고, 멀티스레드 환경에서 안정적인 데이터 방출을 보장합니다.
hoosfa.tistory.com
반응형
'IT기술 > webflux (reactor)' 카테고리의 다른 글
| WebFlux Context 완벽 가이드: 리액티브 프로그래밍에서의 상태 관리와 데이터 공유 (0) | 2025.07.06 |
|---|---|
| Java Optional 완벽 가이드: null 안전성을 위한 차세대 프로그래밍 패러다임 (0) | 2025.07.04 |
| [WebFlux] 리액티브 스트림즈 Sinks: 핵심 개념과 활용 가이드 (0) | 2025.04.28 |
| [WebFlux] Backpressure란? – 리액티브 데이터 흐름 제어의 핵심 (6) | 2025.04.27 |
| WebFlux Cold Sequence와 Hot Sequence: 데이터 흐름의 두 가지 방식 (0) | 2025.04.21 |