reactor와 coroutine의 관계에 대하여 알아봅시다

작성해놓고보니 이전에도 비슷한 포스팅을 했었다.

 

리액터

프로젝트 리액터는 JVM 환경에서 리액티브 애플리케이션을 개발할 수 있도록 지원하는 오픈 소스 프레임워크이다.

 

coroutine

코루틴은 실행을 일시 중지(suspend)하고 재개할 수 있는 컴퓨터 프로그램 구성 요소로, 협동적 멀티태스킹을 위한 서브루틴입니다.

 

둘의 공통점이 있다.

  • 스레드를 오래 붙잡지 않는다
  • 작업이 끝날 때까지 기다리지 않는다
  • 높은 동시성을 목표로 한다

 

이런 공통점 때문에 두 개가 같은 개념인 줄 착각했는데...

spring boot에서 리액터와 coroutine은 둘 다 동작하지만 동작 방식이 약간 다르다.

리액터를 먼저 살펴보자

  • Lazy 방식으로 동작해서, subscribe()가 호출되기 전까지는 연산자가 실행되지 않는다.

이 말을 이해하기 위해서 아래 예제 코드를 보자.

fun main() {
	service() // A가 출력된다 // B는 출력되지 않는다 (subscribe이 되지 않았기 때문에)
    println("C") // C가 출력된다
}

fun service(): Mono<Int> { // 리액터는 Mono와 Flux 타입을 사용한다.
    println("A") // A가 출력된다
    return Mono.just(1)
        .map {
            println("B") // subscribe가 되지 않았기 때문에, B는 출력되지 않는다.
            it + 1
        }
}

main()함수가 실행되면 이 코드는

A

C

를 출력한다.

B는 출력하지 않는다. subscribe()가 되지 않았기 때문이다.

fun main() {
	service().subscribe() // A가 출력된다 // subscribe()를 붙여주었기 때문에, B가 출력된다!
    println("C") // C가 출력된다.
}

main함수를 위처럼 고치면 A B C가 순서대로 출력된다.

 

그러면 코루틴은 어떨까?

suspend fun main(args: Array<String>) {
    serviceB() // A가 출력된다 // B가 출력된다
    println("C") // C가 출력된다
}

suspend fun serviceB(): Int {
    println("A")
    val x = 1
    println("B")
    return x + 1
}

 

main을 실행하면 A B C 가 차례대로 출력된다.

subscribe()같은게 없어도 말이다~~

 

그럼 spring boot에는 왜 이 둘이 섞여있는거임?

spring webFlux는 Reactor 기반 프레임워크다.

코루틴은 추가레이어 정도의 개념이다.

[Netty Event Loop]
        ↓
     Reactor
        ↓
   (옵션) Coroutine Adapter

event roop에 대해서는 이 블로그를 참고...

suspend라는 키워드가 있을 경우 스프링은 실행 모델을 명확히 할 수 있다.

코루틴 실행 모델을 쓰라고 할 수 있는 것.

그걸 CoroutineHandlerAdapter 라고 하는데...

 

CoroutineHandlerAdapter

Spring WebFlux 에서는 어떻게 Kotlin Coroutine 을 지원하고 있을까? ( feat. Context )

(아래 내용은 위 블로그의 내용을 내가 지금 이해하기 위해서 간략히 편집한 것으로 원 포스팅을 읽어봅시다)

 

WebFlux 에서 HTTP 요청의 흐름은 아래 순서대로 흘러간다.

1. org.springframework.web.server.WebFilter 
2. org.springframework.web.reactive.DisptacherHandler
3. org.springframework.web.reactive.HandlerAdapter

 

HandlerAdpator 의 구현체인 RequestMappingHandlerAdapter의 invoke 함수를 따라가보면

invoke target method 가 suspend 함수인지 여부를 체크하여 suspend 함수라면 별도의 invoke 메서드를 호출

여기에서 핵심은 Mono 에서 Coroutine 으로 변환된다.

심지어 Reactor 에서 관리되는 Context 들을 CoroutineContext 로 변환하여 저장할 수 있도록 하고 있다.

즉, Reactor 진영에서 관리하는 객체들을 Coroutine 진영의 객체로 변환하여 가져올 수 있다는 것을 의미한다.