Spring의 비동기 Event Loop기반의 표준 기술인 WebFlux를 통해서 한정적인 Tomcat Thread를 더욱 효율적이고 고사양적으로 사용할 수 있을지 확인.
Spring에서 제공하는 publishOn()
메소드 혹은 DB non-blocking 접근을 지원하는 라이브러리들이 개발되고 있다.
다만 어플리케이션 로직에서 blocking 으로 처리되고 있는 부분이 있다면, 오히려 성능이 떨어지거나 추가적인 이슈가 발생해 Spring MVC 모델보다 비효율적일 수 있다.
build.gradle
dependencies {
// Spring Boot 2.3.8.RELEASE
['spring-boot-starter-web', 'spring-boot-starter-webflux'].each {
implementation "org.springframework.boot:$it"
}
}
application.yml
# 기본 200
server:
tomcat:
threads:
max: 2
RestController
@RestController
@Slf4j
public class IndexApiController {
private final WebClient webClient = WebClient.builder().build();
@GetMapping("/{delay}")
public Mono<String> index(
@PathVariable int delay
) {
Mono<String> result = webClient.get()
.uri("http://postman-echo.com/delay/" + delay)
.retrieve()
.bodyToMono(String.class);
log.info("non blocking - Delay");
return result;
}
@GetMapping("/blocking/{delay}")
public String blocking(
@PathVariable int delay
) {
log.info("blocking - Delay");
return webClient.get()
.uri("http://postman-echo.com/delay/" + delay)
.retrieve()
.bodyToMono(String.class)
.block();
}
}
호출1 - 3개의 request : blocking 형태로 10초 delay API
[io-10999-exec-1] c.controller.IndexApiController : blocking - Delay
[io-10999-exec-2] c.controller.IndexApiController : blocking - Delay
.
.
.
[io-10999-exec-1] c.controller.IndexApiController : blocking - Delay
11689ms
, 10453ms
, 18964ms
소요.
최대 thread 2개이므로, 2개의 request를 처리하는 동안 3번째 요청은 대기하게 된다. 2개중 1개의 요청을 처리한 이후 3번째 요청을 처리해서 결국 약 18.9초의 응답 시간으로 반환.
호출2 - 3개의 request 호출 : WebFlux를 통해 non-blocking 형태로 10초 delay API
[io-10999-exec-2] c.controller.IndexApiController : non blocking - Delay
[io-10999-exec-2] c.controller.IndexApiController : non blocking - Delay
[io-10999-exec-2] c.controller.IndexApiController : non blocking - Delay
10484ms
, 10437ms
, 10427ms
소요.
request를 받은 thread가 API를 호출하고 blocking 되지 않고 바로 반환되기 때문에 3번째 요청도 대기 없이 처리. 1개의 thread만으로도 충분히 요청과 응답을 처리.
non-blocking Event Loop 방식을 통해 효율적인 thread 사용이 가능.
적은 thread 개수로 처리가 가능하여 context switching 비용 감소.
대량의 request를 처리해야 하며, non-blocking 적인 액션들로 구성이 가능한 프로젝트에서 큰 성능을 발휘할 것으로 생각된다.