MSA에서는 어떤 작업이든 Lazy하게 처리하는게 좋다고 한다. 사실 MSA만의 이야기는 아닌거 같고..
Lazy하게 처리한다는건 결국 필요할 때! 필요한 만큼의 리소스만 써서! 필요한 만큼의 데이터만 주자!하는 거니까 어디에든 적용될 수 있는 이야기라는 생각이 든다.
아무튼.. WebFlux를 처음 학습할 때 가장 어려웠던 개념 중 하나가
defer() 였는데, 좋은 예제를 봐서 그런지 아니면 이제 이해가 좀 된건지 몰라도 정리가 되어서 써보는 글...
그 전에 Cold/Hot Publisher 개념에 대해서 한 번 얘기하고 넘어가자
결국은 Lazy한 처리와 관련이 있다.
좀 더 쉬운 이해를 위해 리액터 공식 홈페이지의 말을 인용하면,
Think of an HTTP request: Each new subscriber triggers an HTTP call, but no call is made if no one is interested in the result.
Hot publishers, on the other hand, do not depend on any number of subscribers. They might start publishing data right away and would continue doing so whenever a new Subscriber comes in (in which case, the subscriber would see only new elements emitted after it subscribed). For hot publishers, something does indeed happen before you subscribe.
여기서는 HTTP 요청에 대한 처리를 Cold Publisher로 비유했는데, HTTP 요청에 대한 응답은 client가 요청을 보낼 때, 즉 이 값이 관심이 있을 때에만 보내진다. 이게 Cold Publisher의 개념이다.
어딘가에서 Cold Publisher를 dedicated publisher, 즉 subscriber에 헌신적인 publisher라고 표현한 걸 본 적이 있는데, 이 말이 참 이해하기 좋은 것 같다. 정리하면 Cold Publisher는, 필요한 때에 필요한 사람(?)에게 헌신적인 데이터를 제공하여 작업을 Lazy하게 처리한다.
또, 헌신적이라는 말처럼, 필요한 사람이 여러명이라면 해당 작업을 요청이 있을 때마다 반복한다.
한편, Hot Publisher는 이와 반대로, subscriber의 수나, 필요한 타이밍에 연연하지 않는다. 즉 Hot Publisher는 즉시 데이터를 만들어서 넘겨주고, 때로는 이로 인해 뒤늦게 구독한 구독자가 데이터의 일부만 받게되는 경우도 있다. 즉 Hot Publisher는 데이터를 Eager하게 처리한다.
그래서 이 이야기가 Mono.defer와 무슨 상관이냐? Mono.defer는 Hot Publisher가 Cold Publisher로 전환되도록 도와준다.
아래의 예제 코드를 살펴보자.
// 1
return someClient.getData() // 보통은 api call로 받은 데이터
.switchIfEmpty(Mono.error(new Exception()));
// 2
return someClient.getData()
.switchIfEmpty(defer() -> Mono.error(new Exception()));
이런 코드가 있을 때 1번 상황의 경우 Exception을 생성하는 부분이 이 코드가 실행될 때마다 실행되는 한편,
2번 상황의 경우에는 실제로 구독이 있을때, 즉 받은 데이터가 empty인 경우에만 Exception이 생성된다.
만약 예외를 던지는 코드가 아니라 함수를 호출하는 코드라면? 함수 호출 횟수가 달라질 수도 있다.
결론은..... Mono.defer와 같은 테크닉을 잘 활용해서,
최대한 Lazy하게 작업을 처리하는 코드를 짤 수 있도록 해봐야겠다!
참고 자료
https://www.baeldung.com/java-mono-defer
https://timewizhan.tistory.com/entry/Reactor-defer%EB%9E%80
https://projectreactor.io/docs/core/snapshot/reference/#reactor.hotCold