코루틴을 사용하더라도 Spring MVC의 블로킹 구조로 인해 쓰로풋을 향상시키기 어렵다는 사실을 이전 실험에서 다시 한번 확인했다. 그렇다면 Spring MVC의 블로킹 구조가 왜 쓰로풋 향상을 막는지, 반면에 Spring WebFlux는 어떻게 쓰로풋을 향상시킬 수 있는지 살펴보겠다. 본격적인 분석에 앞서 코루틴의 동작 방식에 대해 간단히 짚고 넘어가려 한다.

 

코루틴은 어떻게 동작하며 왜 사용하는 것일까?

코틀린의 코루틴은 탈출 지점을 기준으로 코드를 여러 조각(코드 블록)으로 나누고, 쓰레드 위에서 중단과 재개를 반복하며 실행할 수 있게 해준다. 이렇게 나눠진 코드 블록이 중단되었다가 다시 재개될 수 있는 이유는 코틀린 컴파일러가 CPS(Continuation-Passing Style) 패턴으로 변환하기 때문이다. CPS 패턴에서는 코드가 실행 도중 특정 지점에서 멈출 수 있으며, 이후 해당 지점부터 다시 시작할 수 있도록continuation을 전달하여 루틴(함수)이 탈출과 진입을 반복할 수 있게 해 준다.

(코틀린 컴파일러가 코루틴 함수를 어떻게 cps 패턴으로 변환하는지 알고 싶으면 Java로 decompile 해보는 걸 추천한다.)

 

이 방식 덕분에 코루틴은 쓰레드가 블로킹될 수 있는 구간을 탈출하고 다른 코드 블록을 실행하며 쓰레드가 놀고 있는 시간을 최소화할 수 있어, 보다 효율적으로 쓰레드를 활용할 수 있다. 이를 통해 적은 수의 쓰레드로도 높은 동시성을 유지할 수 있으며, 이것이 코루틴을 사용하는 주요한 이유이다.

추가적으로, 코루틴은 멀티스레드 환경에서 작업을 병렬로 분산하여 실행할 수 있으며, 동일한 코루틴 내의 코드 블록이라도 각 블록이 서로 다른 스레드에서 실행될 수 있다는 특징이 있다. (그림 2)

그림 1. 싱글쓰레드 기반

 

그림 2. 멀티 쓰레드 기반

 

 

 

MVC 환경에서 코루틴이 쓰로풋 향상에 도움이 되지 못한 이유

MVC 환경에서는 요청당 하나의 서블릿 스레드가 배정되어 처리된다. 서블릿 스레드는 요청 처리가 완료될 때까지 대기하며, 이는 서블릿이 블로킹 IO 모델을 사용하기 때문이다. 따라서, 코루틴을 사용해 비동기 논블로킹 코드를 구현하더라도 블로킹 구간이 존재하게 되어 쓰루풋을 높이는 데 한계가 생긴다.

  

 

 

 

WebFlux환경에서는 코루틴 사용이 쓰로풋을 향상 시킬 수 있었던 이유

WebFlux는 MVC와 달리 요청이 블로킹되는 구간을 없앨 수 있다. 이는 Netty의 이벤트 모델 덕분인데, 요청이 들어오면 채널이라는 객체를 생성해 이벤트 루프에 할당된다. 이후 채널로 요청이 들어오면, 이벤트 루프는 NIO Selector를 통해 이를 감지하고, 해당 요청을 이벤트 큐에 태스크로 등록한다. 등록된 태스크는 순서가 되면 이벤트 큐에서 꺼내져 처리된다. 이렇게 요청의 등록과 처리가 독립적으로 분리되어 비동기적으로 처리할 수 있게 된다. 또한, 요청 처리하는 부분이 코루틴으로 구현되어 블로킹이 발생하지 않기 때문에, 요청을 받고 처리한 후 응답하는 전체 과정에서 스레드가 블로킹되지 않아 쓰로풋이 크게 향상되게 된다.

 

 

 

 

추가적으로, 코루틴이 이벤트 루프를 통해 비동기 논블로킹을 구현하는 과정을 설명하자면, 코루틴으로 작성된 코드는 쓰레드 위에서 실행되다가 블로킹 지점에 도달하면, 재개할 태스크를 이벤트 큐에 등록한 후 쓰레드를 반환하게 된다. 반환된 쓰레드는 이벤트 큐에서 꺼내진 새로운 태스크가 할당되어 다시 실행되며, 이 과정이 반복되면서 블로킹 없이 연속적으로 태스크를 처리할 수 있게 된다.

 

 

(정리하면)

Netty의 이벤트 루프 + 코루틴을 사용하면,

1. 요청의 등록과 처리를 독립적으로 분리하여 비동기적으로 처리할 수 있고

2. 코루틴을 통해 요청에 대한 처리를 비동기 논블로킹으로 처리할 수 있어진다.

3. 따라서 요청을 처리하는 전 과정에서 블로킹이 일어나지 않아 쓰로풋이 향상될 수 있다.

(물론, 코루틴을 사용하더라도 논블로킹을 지원하지 않는 IO를 사용할 경우 블로킹이 발생하여 쓰로풋이 크게 저하될 수 있으니 주의해야 한다.)

 

마치며

이 글에서는 Netty의 이벤트 루프 모델과 코루틴이 만나 어떻게 쓰로풋을 향상시키는지 고수준에서 설명했으며, MVC 환경에서는 구조적인 문제로 인해 코루틴을 사용하더라도 쓰로풋 향상이 어려운 이유를 다루었다. 이를 통해 MVC 환경이 아닌 WebFlux 환경에서 코루틴 사용이 권장되는 이유를 명확히 이해하는 데 도움이 되었기를 바래본다.

 

마지막으로, Netty의 동작 방식은 실제로 훨씬 복잡하고 이해할 부분이 많다. Netty 공식 문서나 Netty In Action과 같은 참고할 만한 자료들이 있으니, 관심이 있다면 한 번 읽어보시기를 추천한다.

+ Recent posts