작성
·
67
·
수정됨
1
WebFlux의 Non-Blocking의 장점이
"여러 작업에 대해서 단일 스레드로 이벤트 루프 방식으로 Blocking 없이 여러 작업을 번갈아 가며 처리 가능하다"라고 이해를 했습니다.
강의의 예제도 보았고 또 여러 예제를 만들어 돌려 보아도,
단일 스레드로 여러 subscribe() 요청을 번갈아가면서 처리하는 것을 확인하지를 못하였습니다.
물론 publishOn이나 subscribeOn, parallel을 이용 혹은
flatMap과 delayElements를 사용하여 별도의 스레드를 띄워 구현한다면
여러 스레드가 병렬로 동시에 여러 요청의 작업을 처리 할 수 있다는 것은 알고 있습니다.
하지만 이 방법은 결국 다수 스레드를 생성하여 각각의 스레드가 각각의 작업을 처리 하는 구조가 되어
Tomcat기반의 동기 MVC 방식에서의 Blocking 멀티스레드와 별반 차이를 체감하지 못하였습니다.
예를 들어 reactor.netty.ioWorkerCount=1 로 설정하고
Rest API "/api/stream" 를 만들어 클라이언트 A가 GET 호출로 구독 시
클라이언트 B는 A가 연결을 끝날때 까지 대기하였습니다. (Blocking MVC와 동일 동작)
(기대한 것은 단일 스레드로 클라이언트 A와 B의 요청을 번갈아가며 처리)
또다른 테스트로
main메소드에서 퍼블리셔에 대해 2개의 subscriber가 거의 동시 구독하였을 경우
먼저 시작한 subscriber의 처리가 다 끝나고 그다음 subscriber가 처리 진행하였습니다.
(물론 스케줄러나 delayElements, webclient 등 내부적으로 별도 스레드를 띄우면 병렬 처리 됨)
[제가 테스트한 결과 1 - 단일 스레드 순차 수행]
[main] WebFluxExample - main - A1 시작
[main] WebFluxExample - main - A2 완료
[main] WebFluxExample - main - A3 완료
[main] WebFluxExample - main - A 체인 최종 결과: A1 -> A2 -> A3
[main] WebFluxExample - main - B1 시작
[main] WebFluxExample - main - B2 완료
[main] WebFluxExample - main - B3 완료
[main] WebFluxExample - main - B 체인 최종 결과: B1 -> B2 -> B3
[제가 테스트한 결과 2 - 멀티 스레드 병렬 수행]
[main] DEBUG reactor.util.Loggers - Using Slf4j logging framework
[boundedElastic-1] WebFluxExample - boundedElastic-1 - A1 시작
[boundedElastic-2] WebFluxExample - boundedElastic-2 - B1 시작
[boundedElastic-1] WebFluxExample - boundedElastic-1 - A2 완료
[boundedElastic-2] WebFluxExample - boundedElastic-2 - B2 완료
[boundedElastic-1] WebFluxExample - boundedElastic-1 - A3 완료
[boundedElastic-2] WebFluxExample - boundedElastic-2 - B3 완료
[boundedElastic-1] WebFluxExample - boundedElastic-1 - A 체인 최종 결과: A1 -> A2 -> A3
[boundedElastic-2] WebFluxExample - boundedElastic-2 - B 체인 최종 결과: B1 -> B2 -> B3
[원하는 결과 - 단일 스레드 병렬 수행]
[동일스레드] WebFluxExample - 동일스레드- A1 시작
[동일스레드] WebFluxExample - 동일스레드- B1 시작
[동일스레드] WebFluxExample - 동일스레드- A2 완료
[동일스레드] WebFluxExample - 동일스레드- B2 완료
[동일스레드] WebFluxExample - 동일스레드- A3 완료
[동일스레드] WebFluxExample - 동일스레드- B3 완료
[동일스레드] WebFluxExample - 동일스레드- A 체인 최종 결과: A1 -> A2 -> A3
[동일스레드] WebFluxExample - 동일스레드- B 체인 최종 결과: B1 -> B2 -> B3
하여 진정한 Webflux의 Non Blocking, 즉 단일 스레드로 (동시 병렬이 아닌) 여러 작업을 번갈아가며 처리하는 것이 가능하다면 처리하는 예제와 결과를 보여주시면 감사하겠습니다.
답변 3
0
안녕하세요. Non-Blocking에 관해서 질문을 주셨는데요.
WebFlux에서 Non-Blocking이라는 개념은,
Java에서 쓰레드를 추가로 할당해서 동시성의 형태로 여러 요청을 번갈아 처리하는 구조라기보다는,
요청을 처리하는 쓰레드(이벤트 루프)가 특정 작업에 묶이지 않고 자유롭게 움직일 수 있는 상태, 즉 Blocking 되지 않은 상태를 말합니다.
즉, Reactor 체인에서 별도의 쓰레드를 사용하지 않아도, Reactor-Netty 서버는 각 요청을 처리하는 흐름이 Blocking되지 않으면 하나의 이벤트 루프 ㅆ레드로도 여러 요청을 빠르게 처리할 수 있습니다.
하지만 만약 어떤 요청이 순차적으로만 처리되고, 다른 요청이 기다리는 현상이 발생한다면,
그건 보통 요청 처리 로직에서 Blocking 연산이 있거나, CPU 연산이 오래 걸려서
이벤트 루프가 다음 요청으로 넘어가지 못하고 쓰레드가 점유된 상태일 가능성이 큽니다.
즉, WebFlux의 Non-Blocking은 쓰레드가 작업 도중 Blocking 되지 않게 만드는 처리 방식임을 이해하는 것이 중요하다고 생각합니다.
아직 3부를 제작하지 않은 상태여서 Spring MVC와 Spring WebFlux를 비교하는 샘플 테스트에서는 클라이언트에서 단순 for문을 이용해서 요청 처리를 시뮬레이션 했는데요.
만약에 실무적으로 쓰레드가 Blocking 되지 않는다 라는 사실을 확인하려면 여러 클라이언트가 동시 다발적으로 요청을 보내는 상황을 만들어야 하고, 서버측 WebFlux 애플리케이션에서도 DB 등의 I/O까지 Fully Non-Blocking하게 처리하는 상황을 만들어서 테스트 해야하는데, 말씀하신 코드를 지금 다 만들기에는 사실상 무리가 있어서 3부 강의 때 말씀드려야 할 것 같아요.
이점 양해 부탁드리고, Non-Blocking의 의미는 쓰레드가 Blocking 되지 않게 하는것이지 우리가 알고 있는 멀티 쓰레드 작업 처리 방식처럼 동시에 요청을 처리하는것은 아니라는 사실은 꼭 기억을 해주시면 감사드리겠습니다.
0
0
안녕하세요, 인프런 AI 인턴입니다. jchttl님.
WebFlux의 Non-Blocking 특성에 대해 문의 주셨는데요. WebFlux의 Non-Blocking은 정말 중요한 주제입니다. 먼저 몇 가지 기본 사항을 다뤄보겠습니다.
Non-Blocking I/O: WebFlux는 Netty 또는 Undertow와 같은 특정 웹 서버를 기반으로 합니다. 이러한 서버들은 Non-Blocking I/O 모델을 사용하여 하나의 요청이 완료될 때까지 차단되는 대신, 요청을 대기열에 넣고 다른 요청을 처리할 수 있습니다.
이벤트 루프 모델: 이벤트 루프는 단일 스레드에서 동작합니다. 이것은 JavaScript의 Node.js와 같이 ‘단일 스레드에서의 논블록킹 I/O’와 비슷한 방식으로 작동합니다. 그러나 Java 환경에서는 멀티스레딩을 활용하여 더 확장할 수 있습니다.
스레드 모델과 Worker Count: reactor.netty.ioWorkerCount
를 1로 설정한 경우, 단일 워커 스레드는 하나의 요청에 대해 차단되지 않고 즉시 다른 요청을 처리할 수 있는 여지가 있습니다. 그러나, 실제로 클라이언트 A가 긴 작업을 수행하며 차단된다면 그것은 블록킹이 아니라, 스레드 사용의 효율 차이일 수 있습니다.
Schedulers.boundedElastic()
)로 오프로드하여야 합니다.WebFlux를 사용할 때 Non-Blocking을 효과적으로 구현하기 위해서는 메커니즘을 잘 이해하고 추가적인 스레드로 차단 가능한 작업을 오프로드하는 방식으로 설계하는 것이 중요합니다.
질문자님의 이해를 돕고자 지식공유자님의 기존 답변을 참고하여 유사한 답변 링크를 추천해드리고 있습니다. 현재 베타 기능으로 답변이 만족스럽지 않을 수 있는 점 양해 부탁드립니다. 🙏 추가적으로 궁금한 점이 있으시면, 이어서 질문해 주세요. 곧 지식공유자께서 답변해 주실 것입니다.
다른 스레드 풀(예:
Schedulers.boundedElastic()
)로 오프로드하여야 한다고 하셨는데이건 톰캣에서 스레드를 생성하여 병렬로 처리하는것과 비슷하지 않을까요?
"
reactor.netty.ioWorkerCount
를 1로 설정한 경우, 단일 워커 스레드는 하나의 요청에 대해 차단되지 않고 즉시 다른 요청을 처리할 수 있는 여지가 있습니다"라고 하셨는데실제 예제를 부탁드립니다.