예시코드
fun getList(request: ServerRequest): Mono<ServerResponse> {
val lastId = request.queryParam("lastId").orElse("0").toLong()
val size = request.queryParam("size").orElse("20").toInt()
return Mono.fromCallable { admMailService.findAll(lastId, size) }
.flatMap { result -> ServerResponse.ok().bodyValue(result) }
}
위와같이 작성하면 이런 경고가 나온다.

왜일까?
우선 fromCallable 은 블로킹/동기 Callable<T> 작업을 지연 평가되도록 바꾸어 reactive파이프라인에 포함시킬때 사용한다. 쉽게 말해 fromCallable로 감싸면 지연평가로 바뀌면서 Mono등의 파이프라인에 조립시킬 수 있음.
이녀석의 기능은 그게 다다. 오직 작업의 실행 시점을 Mono/Flux 파이프라인이 실제로 실행될때로 지연시킬뿐임. 실행 쓰레드는 여전히 구독이 일어난 (보통은 이벤트 루프가 도는) 바로 그 쓰레드다.
따라서 fromCallable 내부 블로킹 작업이 쓰레드를 블록해버릴 수 있는 가능성은 여전히 존재하고, 그게 이벤트루프가 도는 메인쓰레드라면 비동기 서버라는게 무색하게 다른 작업들을 다 블락해버릴 수 있는 가능성이 존재하는 것임.
보통 WebFlux서버는 쓰레드 풀을 굉장히 작게 유지함. CPU 코어수 * 2 정도로 유지하기 때문에 저런 코드를 짜면 금방 쓰레드가 동날 수 있다.(기아 상태)
따라서 위처럼 작성하고 끝내는게 아니라, fromCallable이 감싼 작업을 별도로 실행할 쓰레드 하나를 보통 손에 같이 들려준다. Reactive에서는 subscribeOn(Schedulers.boundedElastic()) 을 활용해 전용 스케쥴러를 손에 들려줌.
사실 가장 좋은건… R2DBC같은걸 이용해서 애초에 DB조회 작업이 블로킹 작업이 아니게 만드는 것이긴 하다..