본문 바로가기
Delvelopment/Spring

[Spring] WebFlux EventLoop (Non Blocking)

by 제제킴 2022. 12. 4.
반응형

Spring Webflux에서 사용되는 EventLoop를 보기 위해 Spring mvc 에서 사용되는 thread per request에 대해서 비교해 보려고 한다.

두 모델의 차이는 Blocking처리 방식과 Non-Blocking 처리 방식으로 Thead의 idle 시간을 줄여 동시에 더 많은 요청을 처리 할 수 있게된다.

Spring mvc에서는 DispathServlet형태의 Servlet으로 처리되어 톰캣에 의존적입니다. 또한, thead per request방식으로 Servlet Container에서 요청을 Thread에 할당하여 Blocking 방식으로 호출되며 Idle 상태가 됩니다. Blocking 방식으로 idle 상태가 유지되면 많은 요청이 있는 서비스에서는 적절한 처리를 하지 못하게 될 것입니다.

이를 개선하기 위해 Non Blocking API 들이 추가되었으며 Spring WebFlux가 개발된 이유기도 합니다.

그래서 우리는 비동기 논블러킹 방식을 Spring에서 Spring WebFlux를 통해 사용할 수 있습니다.

 

 

 

Spring mvc, Thread per request 모델의 한계

Spring mvc 방식은 다소 복잡하고 일반적으로 Blocking 방식으로 처리됩니다. 예를들어, 데이터를 수정하거나 가져오는 데이터베이스 호출하는 경우에 해당됩니다. (현재 NonBlocking 방식을 지원하는 DB는 몇개 없음)

만약, Spring mvc로 구성된 서버에 여러 요청이 들어온다면, 사용가능한 Thread 수만큼 Request가 처리되며 Blocking IO처리를 할 때 해당 Thread는 아무것도 하지 않고 대기 상태가 됩니다.

Thread의 대기상태(Idle)가 길게되면 많은 요청(Request)를 처리할 수 없게되고 병목이 발생할 수 있습니다.
이를 해결하기 위해 Thread pool을 늘리는 방법도 있겠으나, Core 수에 대비하여 너무 많은 Thread pool을 구성하게 되면, Context Swiching에 대한 Cost도 발생하게 되어 문제가 발생 할 수 있습니다.

그래서, 이러한 문제를 해결하기 위해 (Thread 의 Idle 상태를 줄이기 위해) Reactive Programming이 개발되었습니다.

 

Spring WebFlux, Reactive Programming

Nonblocking은 작업을 기다리기 보다는 완료되거나 데이터를 사용할 수 있게 되면 반응하게 됩니다. 즉, Reactive Programming은 변화에 반응하는 것을 중심을 두고 만들어 졌습니다. (Thread가 Idle 되지 않고 event가 있을때 처리하고 반환하게 됩니다.)

그래서 Reactive programming은 Thread 사용률을 높히고, 높은 동시성을 가지도록 만들어 줍니다.

Application에서 I/O를 요청 후 바로 return되어 다른 작업을 수행하다가 특정 시간에 데이터가 준비가 다되었는지 상태를 확인하며, 데이터의 준비가 끝날 때까지 틈틈이 확인을 하다가 완료가 되었으면 종료된다.
여기서 주기적으로 체크하는 방식을 폴링(Polling) 이라고 한다. 그러나 이러한 방식은 작업이 완료되기 전까지 주기적으로 호출하기 때문에 불필요하게 자원을 사용하게 됩니다.

예를들어, Reactive Model이 사용될 경우, 데이터베이스를 읽는 호출 데이터를 fetch할 동안 API를 호출한 Thread가 Block되지 않고, 해당 호출을 subscribe할 수 있는 publisher를 즉각적으로 반환합니다.
Subscriber는 해당 작업(Fetch)가 끝난 후 이벤트 처리할 수 있고, 다른 event를 생성 할 수도 있습니다.
Non-Blocking Backpressure를 사용해서 처리하는 Reactive Streams 방법이기도 합니다

Event Loop

Event Loop Model은 WebFlux에서 Reactive Programming을 구현되어 있는 방식입니다

  • 이벤트 루프는 few thread에서 계속 동작합니다.
  • 이벤트 루프는 이벤트 큐로부터 이벤트들을 순차적으로 처리하고 알맞은 콜백 함수를 실행합니다.
  • 외부 서비스 호출이나 데이터베이스 호출과 같은 동작의 완료에 의해 트리거가 될 수 있습니다.
  • 이벤트 루프는 IO작업의 완료에 대한 콜백을 발생하고 그 결과를 완래 호출자에게 결과를 보낼 수 있습니다.

Event Loop는 Node.js, Netty, Nginx 등 많은 플랫폼에서 구현되어 있습니다.

 

Spring WebFlux는 전통적인 annotation 기반의 프로그래밍 모델을 함수형 라우팅을 가지고 확장했습니다.
Reacive Stream API 와 HTTP runtimes을 나란히 적용 했습니다. 런타임을 상호 운영할 수 있게 만들었습니다.

Servlet 3.1 + Contrainer를 포함한 다양한 reacive runtime들을 지원 할 수 있습니다. (Tomcat, Reactor, Netty, UnderTow 등)함수형이며 fluent API를 제공하는 HTTP 요청들을 위한 WebClinet가 존재합니다.

Spring WebFlux는 Reactor Netty가 Embeded된 서버 입니다. Netty에 의해 default로 생성되는 쓰레드 들이 존재합니다. (server, reactor-http-nio-1, reactor-http-nio-2, reactor-http-nio-3)
Netty는 반응성 비동기 형태로 확장성 잇는 동시성을 제공하기 위해 Event Loop Model이 적용되어 있습니다.
확장성을 위해 NOI를 이용하여 Event Loop를 구성합니다.

Event Loop Group은 하나 이상의 Event Loop를 관리합니다. 이용가능한 core 수 보다 더 많은 Event Loop가 구성되는 것은 추천되지 않습니다. Context Switching Cost 때문입니다.

EventLoopGroup은 새롭게 추가된 각각의 Channel을 Event Loop에 할당합니다. Channel의 라이프 사이클 동안 모든 동작은 하나의 thread에서 처리되게 됩니다.

Apache Tomcat 에서도 Spring WebFlux가 지원됩니다. ( Servlet 3.1+ API )

Netty 기반의 WebFlux와는 달리 많은 thread들이 존재합니다. Tomcat 5에서는 NIO기반의 Connector를 사용해서 Acceptor thread들을 통해 연결을 처리하고 각 연결을 Poller thread들에 의해서 데이터를 이용할 수 있는지 체크하면 Workerthread에 의해 각 요청을 매핑해서 처리합니다. 즉, 많은 thread가 사용됩니다.

Request per Thread model과 의 차이는 다수의 요청을 적은 Thread로도 처리 할 수 있다. 적은 Thread를 활용해 (Non Blocking 방식) 효율적으로 I/O 를 제어 할 수 있고 성능에도 좋은 영향을 미치게 됩니다.

 

개인적 생각.

Spring mvc 대신 Spring WebFlux를 쓴다고 속도가 빨라질까?
이 질문은 서비스 환경에 따라 다르겠지만, 맞다 틀리다를 정할 수 없는 질문이다.
DB가 R2DBC를 지원하며, 동시성 처리에 적절한 모델이라면 채택이 적절하다고 생각한다.

MSA에 구조에서 각 서비스들 간의 통신을 Non-Blocking 방식으로 사용되면 Thread의 Idle을 감소 시키는 효과도 존재하기에 현재 아키텍처 트랜드와 적절한 기술이라고 보여진다.

현재 Reactive programming을 구성하기 위한 R2DBC(Reactive Relational Database Connecivity)를 지원하는 DB는 NoSQL, Mysql(jasync), Postgres, reactive Redis, MongoDB 등 사실 전통적으로 사용하고 있는 DB가 마이그 되기 전까진 Blocking이 발생한다.

그리고 WebFlux를 사용할때 Blocking 처리가 존재하면 오히려 시스템이 Hang이 걸리는 증상도 존재한다.
결국 시스템 설계 및 개발시 아키텍처 구조 및 서비스에 대한 목적을 고려해 Spring-mvc 와 Sptring-Webflux를 적절하게 채택하는게 알맞은 것 같다.
결국 많은 요청 및 자원을 사용하지 않는 서비스라면 Spring mvc가 Spring Webflux보다 더 빠를 수 있거나 비슷할 것이다.

 

 

 

https://www.youtube.com/watch?v=I0zMm6wIbRI

https://www.infoq.com/articles/Servlet-and-Reactive-Stacks-Spring-Framework-5/

https://docs.spring.io/spring-data/r2dbc/docs/current/reference/html/#get-started:first-steps:what

https://devahea.github.io/2019/04/21/Spring-WebFlux는-어떻게-적은-리소스로-많은-트래픽을-감당할까/

https://velog.io/@jihoson94/EventLoopModelInSpring

반응형

댓글